---
# System prepended metadata

title: 'Python (matplotlib,copy,iter,class,decorator)'
tags: [Coding]

---

###### tags: `Coding`
# Python (matplotlib,copy,iter,class,decorator,env)

## matplotlib
&emsp;在使用fill_between的時候，如果兩條線段的x,y數量不一樣，可以使用內插的方式使得兩縣段一樣，配合np.interp內插，程式碼如下
>線段一:x1,y1<br>
>線段二:x2,y2<br>
>xfill = np.sort(np.concatenate([x1,x2])) ->將x1和x2的x點總和，並且由小排到大(後續內插用這個list內的每個點作為欲內插的x值)<br>
>y1fill = np.interp(xfill,x1,y1) ->利用線段一的x1和y1，內插出符合xfill內每個x值相對應的y值並命名為y1fill<br>
>y2fill = np.interp(xfill,x2,y1)>利用線段二的x2和y2，內插出符合xfill內每個x值相對應的y值並命名為y2fill<br>
>plt.fill_between(xfill,y1fill,y2fill,interpolate=True) ->填充已經將數量化成一樣的xfill,y1fill,y2fill<br>

**&emsp;在使用np.interp的時候必須要由小排到大(只有要內插的線段由小排到大，要插的值不用)，因此如果x值為由大到小，則需要使用[x值][::-1]使得排序顛倒，同理y值也要同樣顛倒才能得到相對應的值[y值][::-1]**

---
## copy
### 賦值/淺複製/深複製

* 賦值：單純利用等號 a = b，將a定義為b的別名，導向相同地址
* 淺複製：利用a = b.copy()，開設新的地址，改變不可變型時，互不影響，改變可變型時仍可影響
* 深複製：利用a=b.deepcopy()，開設新的地址，改變可變型/不可變型時，皆互不影響

**註解：在pandas內，內建的copy應為deep copy)**<br>
**可變型：列表，字典 /不可變型：數字，字串，元組**
範例：
```
a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改變a中的第一個元素，即改變a中的數字
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)
#輸出：
a: [4, [2, 3]] id(a): 4578314360
a_equal: [4, [2, 3]] id(a_equal): 4578314360
a_shallowcopy: [1, [2, 3]] id(a_shallowcopy): 4562436248
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4562362096
#（因為第一個位置為數字，因此copy()/deepcopy()並無差異

a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改變a中的第一個元素，即改變a中的數字
a[1][1] = 5     # 改變a當中第二個元素中的元素，即 改變a當中的list
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)
#輸出：
a: [4, [2, 5]] id(a): 4454672504
a_equal: [4, [2, 5]] id(a_equal): 4454672504
a_shallowcopy: [1, [2, 5]] id(a_shallowcopy): 4438794392
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4438720240
#(因為第二個改變變量為list，為可變型，因此copy()/deepcopy()產生差異)
```

---
## iter,next

```
In python3 shell:
>>> mylist = [2,4,6,8,10]
>>> mylist_iterator = iter(mylist) # 這個iter()函數會調用python預設的mylist.__iter__函數，並回傳一個iterator物件，然後我們用mylist_iterator這個變數來儲存這個iterator物件
>>> # 開始迭代
>>> i = next(mylist_iterator) # i = 2 , next()函數會調用python預設的mylist_iterator.__next__函數
>>> print(i)
2
>>> i = next(mylist_iterator) # i = 4
>>> print(i)
4
>>> i = next(mylist_iterator) # i = 6
>>> print(i)
6
>>> i = next(mylist_iterator) # i = 8
>>> print(i)
8
>>> i = next(mylist_iterator) # i = 10
>>> print(i)
10
>>> i = next(mylist_iterator) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> # 當最後一個元素遍歷完，再呼叫mylist_iterator.__next__就會跑出一個 StopIteration 的例外，一般for in在呼叫__next__的過程中遇到了 StopIteration 就會自動停止 for loop的執行
```

---
## 類別(class)
&emsp;主要為建立一類別，後續可以指定該類別內須包含的屬性(attribution)以及方法(method)，定義範例如下：<br>
```
class car:
#以此方式可定義一類別為car
```
&emsp;物件（object)，藉由指定一物件為特定類別，可將其歸納在該類別內：<br>
```
c200 = car()
```
&emsp;每個被定義在特定類別內的物件，會擁有該類別內的屬性，例如今天將car定義一個屬性顏色，則物件同樣也會有顏色這個屬性，如下<br>
```
class car:
    def __init__(self,color):
        self.color = color

c200 = car (color = 'red')
print(c200.color) # 執行結果：'red'
```
&emsp;方法(method)與函式(function)的結構很像，皆是使用def開頭，然而在方法內由於有指定在特定類別內才可使用，因此在def內必須包含本身(self)，例如:<br>
```
class car:
    def __init__(self,color):
        self.color = color
    
    def run(self):
        print('car is running')
上述中的run即為方法的一種
```
### 方法種類
&emsp;方法可分為三類，如下：<br>
1. 實體方法(Instance Method)
2. 類別方法(Class Method)
3. 靜態方法(Static Method)
4. 抽象方法(Abstract Method)

#### 實體方法(Instance Method)
&emsp;在定義上實體方法為一種不添加裝飾器的方法，會直接指向物件本身，且至少須傳入一個self參數，經由self參數，可以更方便的指向物件內的屬性，以及物件本身的其餘方法，範例如下：<br>
```
# 汽車類別
class Cars:
    # 建構式
    def __init__(self):
        self.color = "blue"
    # 實體方法(Instance Method)
    def drive(self):
        print(f"{self} is {self.color}.")
        self.message()  # 呼叫其他方法
    # 實體方法(Instance Method)
    def message(self):
        print("Message method is called.")
mazda = Cars()
mazda.drive()
```
而由於此種方式是藉由呼叫物件本身以及物件本身的屬性等運作，而非藉由呼叫類別，因此若是直接藉由類別呼叫方法，會藉由編譯器報錯，下面為錯誤範例：<br>
```
Car.drive()
#報錯內容：
TypeError:drive() missing 1 required positional argument:'self'
```
此外，在實體方法內，可使用```self.__class__.attribution```來改變類別屬性，範例如下:<br>
```
# 汽車類別
class Cars:
    door = 4  #類別屬性
    # 實體方法(Instance Method)
    def drive(self):
        self.__class__.door = 5
print("Cars original door: ", Cars.door)
mazda = Cars()
mazda.drive()
print("Cars new door: ", Cars.door)
```
#### 類別方法
&emsp;具有@classmethod裝飾器的方法，在呼叫時指向的是類別本身，須帶有cls參數，而非物件本身，基本架構如下:
<br>
```
# 汽車類別
class Cars:
    # 類別方法(Class Method)
    @classmethod
    def open_door(cls):
        print("Open door is class method.")
```
類別方法，可藉由類別或是物件呼叫，但由於其指向的是類別而非物件，因此在內部做變更時，只能改變到類別本身，而無法影響到物件，範例如下：<br>
```
# 汽車類別
class Cars:
    door = 4  # 類別屬性
    # 類別方法(Class Method)
    @classmethod
    def open_door(cls):
        print(f"{cls} has {cls.door} doors.")
mazda = Cars()
mazda.open_door()  #透過物件呼叫
Cars.open_door()  #透過類別呼叫
#輸出結果：
<class '__main__.Cars'> has 4 doors
<class '__main__.Cars'> has 4 doors
```
類別方法較為特別的是常用於創建物件，其可用於創建物件的原因來自於其作用的是類別本身，因此不會先定義到物件，使用此方法創建物件可以讓程式碼更乾淨整潔，當中會使用到方法的return作為創建的手段，範例如下：<br>
```
# 汽車類別
class Cars:
    # 建構式
    def __init__(self, seat, color):
        self.seat = seat
        self.color = color
    # 廂型車
    @classmethod
    def van(cls):
        return cls(6, "black")
    # 跑車
    @classmethod
    def sports_car(cls):
        return cls(4, "yellow")
van = Cars.van()
sports_car = Cars.sports_car()
```
#### 靜態方法
&emsp;帶有@staticmethod裝飾器的方法，不帶有cls以及self的方法，可傳入任何值，而由於並沒有傳入cls或self，因此靜態方法本身並無法改變類別或是物件本身，基本架構範例如下:<br>
```
# 汽車類別
class Cars:
    #靜態方法
    @staticmethod
    def accelerate():
        print("Accelerate is static method.") 
```
在類別內，靜態方法算是一種獨立方法，常應用於方法(Method)中無需存取物件(Object)的屬性(Attribute)或方法(Method)，單純執行傳入參數或功能上運算的情況，如下範例：<br>
```
# 汽車類別
class Cars:
    # 速率靜態方法
    @staticmethod
    def speed_rate(distance, minute):
        return distance / minute
# 透過物件呼叫
van = Cars()
van_rate = van.speed_rate(10000, 20)
print("van rate: ", van_rate)
# 透過類別呼叫
sports_car_rate = Cars.speed_rate(20000, 20)
print("sports car rate: ", sports_car_rate)

#執行結果：
van rate: 500.0
sports car rate: 1000.0
```
### 內建方法
#### 建構子 ```__init__()```
&emsp;將利用建構子建立出的物件稱為實體(instance)
#### ```__call__()```
&emsp;變成callable的實例，此方法內定義當實例被呼叫時要做的動
作
#### ```__del__()```
&emsp;當時實例被刪除的時候會被呼叫的方法
#### ```__str__()```
&emsp;針對print，若直接使用print(類別)，則會輸出__str__()的回傳值
#### ```__repr__()```
&emsp;針對直接輸入變數，若直接輸入變數，則會輸出__repr__()的回傳值
```
# __str__() vs __repr__()
class test:
    def __init__(self)：
        pass
    def __str__(self):
        return 'this is for str'
    def __repr__(self):
        return 'this is for repr'

a = test()
print(a) #輸出結果: this is for str
a #輸出結果： this is for repr
```


### 繼承
&emsp;在繼承關係上，存有父類別(Base Class)以及子類別(sub class)的關係，在使用上，子類別會附有父類別的屬性以及方法，但父類別不具有子類別獨有的屬性以及方法，以此可降低程式的重複性。<br>
**Python繼承(Inheritance)的重要觀念如下：**
* 如何使用Python繼承(Inheritance)
* 方法覆寫(Method Overriding)
* 多層繼承(Multi-Level Inheritance)
* 多重繼承(Multiple Inheritance)

#### 實踐繼承(Inheritance)
在實際應用上，最基礎的類別繼承方式如下:
```
# 交通工具(基底類別)
class Transportation:
    # 建構式
    def __init__(self):
        self.color = "white"  #顏色屬性
    # 駕駛方法
    def drive(self):
        print("drive method is called.")
# 汽車子類別
class Car(Transportation):
    # 加速方法
    def accelerate(self):
        print("accelerate is method called.")
# 飛機子類別
class Airplane(Transportation):
    # 飛行方法
    def fly(self):
        print("fly method is called.")
```
上式內，先定義出交通工具這一個類別，再將汽車以及飛機繼承到交通工具上，則兩者皆可以呼叫交通工具類別內的drive方法，在判斷是否為父子類關系，可使用python內建的issubclass判斷<br>
#### 方法覆寫(Method Overriding)
&emsp;若是同時在子類別與父類別內出現相同的方法，則會先執行子類別內的方法，而此時若是想要在子類別內再執行父類別內的方法，則可使用super()，範例如下:<br>
```
# 交通工具(基底類別)
class Transportation:
    # 駕駛方法
    def drive(self):
        print("Base class drive method is called.")
# 汽車子類別
class Car(Transportation):
    # 駕駛方法
    def drive(self):
        super().drive()
        print("Sub class drive method is called.")
mazda = Car()
mazda.drive()
#執行結果:
Base class drive method is called. (from super().drive())
Sub class drive method is called. (from children)
#註解:
super()常用於super().__init__()以此方式可直接繼承父類的__init__()
```

#### 多層繼承(Multi-Level Inheritance)
&emsp;將子類作為別的物件的父類，則最終子代會同時附加有其父類以及父類的父類的屬性以及方法。
#### 多重繼承(Multiple Inheritance)
&emsp;一物件不只可同時繼承於一類別，例如今天有一類別為動物，而又有一類別為鳥類，則鴨子可同時繼承動物以及鳥類兩類別，範例如下:<br>
```
# 動物類別
class Animal:
    def eat(self):
        print("Animal eat method is called.")
# 鳥類類別
class Bird:
    def walk(self):
        print("Bird walk method is called.")
# 鴨子類別
class Duck(Animal, Bird):
    pass
duck = Duck()
duck.eat()
#註解:
若是兩父類別中有相同的方法，則會先執行最先繼承的那個類別內的方
法，屬性雷同，因此須注意兩父類別內是否有重複定義的方法或屬性。
```
### 屬性
&emsp;在python內並沒有真正能夠保護變數的機制，但會透過約定成俗的方式將屬性定義成**私有屬性**以及**保護屬性**。<br>
* 保護屬性: 在變數前加上下底線，例如_var
* 私有屬性: 在變數前加上兩下底線，例如__var
* 
-------------
## 裝飾器
<a href ='https://ithelp.ithome.com.tw/articles/10200763'>reference 1 </a><br>
<a href = 'https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-3-%E7%A5%9E%E5%A5%87%E5%8F%88%E7%BE%8E%E5%A5%BD%E7%9A%84-decorator-%E5%97%B7%E5%97%9A-6559edc87bc0'>reference 2  </a><br>
裝飾器基本範例如下：<br>
```
def printHello(func):
    def wrapper():
        print('Hello')
        return func()
    return wrapper

@printHello
def printWorld():
    print('World')

printWorld()
```
&emsp;在上述程式碼中，@在python內被稱為語法糖（Syntax Candy)，主要用處為讓語法更加簡潔，程式碼在裝飾器內會將函式作為變數傳入，於該函式內定義另一函數，將新定義的函式作為返回值，以此呼叫新定義的函數，新定義的函數內執行完該函數後，會返回原先傳入的變數（最初使的函式），以此先執行裝飾器外的函數。流程大致如下:<br>
> 定義函式a並套上裝飾器 -> 執行裝飾器本身的函式b ->裝飾器內定義函式c並返回函式c <br>->執行函式c ->函式c返回函式a ->執行函式a

&emsp;然而若今天函式a有變數傳入該如何處理呢？處理方式為將其變數同時傳入於函式c當中，以此變可以讓函數a在執行裝飾器過程中也吃到變數，範例如下:<br>
```
def printHello(func):
    def wrapper(arg):
        print('Hello')
        return func(arg)
    return wrapper

@printHello
def printArg(arg):
    print(arg)

printArg('World')
printArg('Kitty')
```
&emsp;而若是今天參數很複雜，則可使用不定參數傳入(```*arg```、```**kwarg```)，範例如下:<br>
```
def printHello(func):
    def wrapper(*args, **kwargs):
        print('Hello')
        return func(*args, **kwargs)
    return wrapper

@printHello
def printSingle(arg):
    print(arg)

@printHello
def printDouble(arg1, arg2):
    print(arg1)
    print(arg2)

printSingle('World')
printDouble('Kitty', 'Danny')
```
&emsp;若是今天想要在裝飾器內也傳入參數，則可以同等邏輯，在裝飾器外再包一層函式，並將參數傳入內部，範例如下:<br>
```
def printArg(arg):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(arg)
            return func(*args, **kwargs)

        return wrapper

    return decorator

@printArg('Hi')
def sayHiAndPrintArg(arg):
    print(arg)


sayHiAndPrintArg('World')
```
&emsp;比較特別的是裝飾器是有順序性的，若今天包了兩層裝飾器給一函式，則會以最接近的裝飾器開始向外包裝，範例如下:<br>
```
def print_func_name(func):
    def warp_1():
        print("Now use function '{}'".format(func.__name__))
        func()
    return warp_1


def print_time(func):
    import time
    def warp_2():
        print("Now the Unix time is {}".format(int(time.time())))
        func()
    return warp_2


@print_func_name
@print_time
def dog_bark():
    print("Bark !!!")


@print_time
@print_func_name
def cat_miaow():
    print("Miaow !!!")


if __name__ == "__main__":
    dog_bark()
    
    #輸出結果:
    # > Now use function 'warp_2'
    # > Now the Unix time is 1541239747
    # > Bark !!!

    cat_miaow()
    # > Now the Unix time is 1541239747
    # > Now use function 'cat_miaow'
    # > Miaow !!!
```
&emsp;在上述程式碼中，dog的部份會先包在print_time內，再被包在print_func_name內，而在執行時，則會由外向內執行，也就是會先print出```
"Now use function warp_2```，後在print出```Now the Unix time is ```
，最後才印出Bark，在這邊由於print_func_name是吃到print_time的回傳值warp_2作為變數傳入，因此吃到的function name就會是warp_2，同理在cat的部份則會反過來，先print``` Now the Unix time is```，再print``` Now use function cat_miaow```，最後才print出Miaow




----

