###### tags: `Coding`
# Python (matplotlib,copy,iter,class,decorator,env)
## matplotlib
 在使用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>
** 在使用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)
 主要為建立一類別,後續可以指定該類別內須包含的屬性(attribution)以及方法(method),定義範例如下:<br>
```
class car:
#以此方式可定義一類別為car
```
 物件(object),藉由指定一物件為特定類別,可將其歸納在該類別內:<br>
```
c200 = car()
```
 每個被定義在特定類別內的物件,會擁有該類別內的屬性,例如今天將car定義一個屬性顏色,則物件同樣也會有顏色這個屬性,如下<br>
```
class car:
def __init__(self,color):
self.color = color
c200 = car (color = 'red')
print(c200.color) # 執行結果:'red'
```
 方法(method)與函式(function)的結構很像,皆是使用def開頭,然而在方法內由於有指定在特定類別內才可使用,因此在def內必須包含本身(self),例如:<br>
```
class car:
def __init__(self,color):
self.color = color
def run(self):
print('car is running')
上述中的run即為方法的一種
```
### 方法種類
 方法可分為三類,如下:<br>
1. 實體方法(Instance Method)
2. 類別方法(Class Method)
3. 靜態方法(Static Method)
4. 抽象方法(Abstract Method)
#### 實體方法(Instance Method)
 在定義上實體方法為一種不添加裝飾器的方法,會直接指向物件本身,且至少須傳入一個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)
```
#### 類別方法
 具有@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()
```
#### 靜態方法
 帶有@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__()```
 將利用建構子建立出的物件稱為實體(instance)
#### ```__call__()```
 變成callable的實例,此方法內定義當實例被呼叫時要做的動
作
#### ```__del__()```
 當時實例被刪除的時候會被呼叫的方法
#### ```__str__()```
 針對print,若直接使用print(類別),則會輸出__str__()的回傳值
#### ```__repr__()```
 針對直接輸入變數,若直接輸入變數,則會輸出__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
```
### 繼承
 在繼承關係上,存有父類別(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)
 若是同時在子類別與父類別內出現相同的方法,則會先執行子類別內的方法,而此時若是想要在子類別內再執行父類別內的方法,則可使用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)
 將子類作為別的物件的父類,則最終子代會同時附加有其父類以及父類的父類的屬性以及方法。
#### 多重繼承(Multiple Inheritance)
 一物件不只可同時繼承於一類別,例如今天有一類別為動物,而又有一類別為鳥類,則鴨子可同時繼承動物以及鳥類兩類別,範例如下:<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()
#註解:
若是兩父類別中有相同的方法,則會先執行最先繼承的那個類別內的方
法,屬性雷同,因此須注意兩父類別內是否有重複定義的方法或屬性。
```
### 屬性
 在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()
```
 在上述程式碼中,@在python內被稱為語法糖(Syntax Candy),主要用處為讓語法更加簡潔,程式碼在裝飾器內會將函式作為變數傳入,於該函式內定義另一函數,將新定義的函式作為返回值,以此呼叫新定義的函數,新定義的函數內執行完該函數後,會返回原先傳入的變數(最初使的函式),以此先執行裝飾器外的函數。流程大致如下:<br>
> 定義函式a並套上裝飾器 -> 執行裝飾器本身的函式b ->裝飾器內定義函式c並返回函式c <br>->執行函式c ->函式c返回函式a ->執行函式a
 然而若今天函式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')
```
 而若是今天參數很複雜,則可使用不定參數傳入(```*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')
```
 若是今天想要在裝飾器內也傳入參數,則可以同等邏輯,在裝飾器外再包一層函式,並將參數傳入內部,範例如下:<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')
```
 比較特別的是裝飾器是有順序性的,若今天包了兩層裝飾器給一函式,則會以最接近的裝飾器開始向外包裝,範例如下:<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 !!!
```
 在上述程式碼中,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
----