###### 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 ----