# :memo: Python ###### tags: `Python` ## Reserved keyword ![](https://i.imgur.com/oWdAWXD.png) ## Naming Rules and Conventions ```python= MACRO_CASE # constants current_salary # variables snake_case ``` ## Value and Variable reference in memory ![](https://i.imgur.com/ImzarR5.png) ```python= >>> a = 2 # 建立 2 value 並將 a variable 關聯過去 >>> id(a) 4343310672 >>> a += 1 # 得到新的 value 並將 a variable 重新關聯 >>> id(a) 4343310704 >>> id(3) 4343310704 >>> b = 2 # 2 value 已經建立過,直接將 b variable 關聯過去 >>> id(2) 4343310672 >>> id(b) 4343310672 ``` ```python= >>> def a(): # 定義 function object 也有位址,以 a 進行參考 ... pass ... >>> b = a # b 也參考到 a 的對象,即 function object >>> b() >>> id(a) # a, b 參考到的 object 是同一個 4340704736 >>> id(b) 4340704736 ``` ## Scope > - 在 function 中有 local names > - 在 module 有 global names > - 在最外面有 built-in names ```python= def outer_function(): a = 20 def inner_function(): a = 30 print('inner_function, a =', a) # a is local a in nested function inner_function() print('outer_function, a =', a) # a is local a in function a = 10 outer_function() print('module, a =', a) # a is global in model ``` > 發生 reference before assignment 的問題 > > - 該變數在該 scope 只 read-only 時,會 lookup global variable > - 該變數在該 scope 有 assignment 時,視為 local variable > - 該 local variable 沒任何資料下是不能被參考使用的 ```python= x = 10 def foo(): x = x * 2 # reference before assignment print(x) def bar(): y = x * 2 # reference before assignment x = y # python 掃到 x 被 assign,視 x 為 local variable # x * 2 一個不存在的 local variable 被讀取 # UnboundLocalError: local variable 'x' referenced before assignment print(x) def qoo(): y = x * 2 # ok, treat x as global x # python 沒找到 x ,視 x 為 global variable # 將算完的結果放到 y print(y) #foo() #bar() qoo() ``` > 使用 gloabl keyword 來定義該變數為 gloabl ```python= x = 10 def foo(): global x # 告訴 python 這個參考到 global x x = x * 2 print(x) # 20 print(x) # 10 foo() print(x) # 20 ``` ## Conversion and Casting ```python= >>> float(5) 5.0 >>> int(10.6) 10 >>> int(-10.6) -10 >>> float('2.5') 2.5 >>> str(25) '25' >>> set([1,2,3,3]) # list to set {1, 2, 3} >>> tuple({1,3,3,4}) # set to tuple (1, 3, 4) >>> list('hello') # string to list ['h', 'e', 'l', 'l', 'o'] >>> dict([[1,2], [3,4]]) # list of list to dict {1: 2, 3: 4} >>> dict([(1,2), (3,4)]) # tuple of list to dict {1: 2, 3: 4} ``` > - Implicit Type Conversion > - python 自動轉型 > - ex: int to float (123 + 123.4 = 246.4) > - 數字和字串不同型別,不能自動轉型,發生 **TypeError** > - Explicit Type Conversion > - 使用 <required_datatype>(expression) 指示轉型 > - int() > - str() > - float() > - bool() | + | int | float | bool | string | | - | - | - | - | - | | int | int | float | int | X | | float | float | float | float | X | | bool | int | float | int | X | | string | X | X | X | string | ## Syntax ### Operator - Arithmetic operators ![](https://i.imgur.com/LZGiFLp.png) - Comparison operators ![](https://i.imgur.com/OnGO3U1.png) - Logical operators ![](https://i.imgur.com/3eWmeZ2.png) - Bitwise operators ![](https://i.imgur.com/C47yHST.png) - Identity operators ![](https://i.imgur.com/CFPIvCZ.png) - Membership operators ![](https://i.imgur.com/SJetjL0.png) ```python= >>> (1+100) * 100/2 5050.0 >>> (1+100) * 100//2 # 雙斜線取整數 5050 ``` ```python= >>> 10**2 # 次方 100 ``` ```python= # type 可以顯示 object 為那種類型 >>> type(10.0) <class 'float'> >>> type('hello') <class 'str'> >>> type('bool') <class 'str'> >>> type(None) <class 'NoneType'> ``` ```python= # id 可以顯示變數的位址 >>> a = 10 >>> id(a) 4338297424 >>> b = 20 >>> id(b) 4338297744 >>> b = a # b 也參考到 a 的位址 >>> id(b) 4338297424 ``` ### Assignment ```python= >>> a = 10 >>> b = 20 >>> a, b = b, a # swap, unpack assign >>> a 20 >>> b 10 >>> ``` ```python= >>> a, b, c = 5, 3.2, "Hello" >>> x = y = z = "same" ``` ### Constants ```python= PI = 3.14 GRAVITY = 9.8 ``` ### Docstrings ```python= def double(num): """Function to double the value""" return 2*num ``` ### Flow #### if ```python= if num > 0: print(num, "is a positive number.") ``` ```python= if num >= 0: print("Positive or Zero") else: print("Negative number") ``` ```python= if num > 0: print("Positive number") elif num == 0: print("Zero") else: print("Negative number") ``` #### for ```python= >>> count = 0 >>> for i in range(1, 101): ... count += i ... >>> count 5050 ``` > for 可以搭配 else > - 當條件為 False 時執行 else > - else 會執行是 for loop 沒有任何 break 發生時 > - 當 break 發生時 else 不執行 ```python= student_name = 'James' marks = {'James': 90, 'Jules': 55, 'Arthur': 77} for student in marks: if student == student_name: print(marks[student]) # result: 90 break else: # 不會執行,因為發生了 break。只有當 break 沒發生時才會執行 print('No entry with that name found.') ``` #### while ```python= n = 10 # initialize sum and counter sum = 0 i = 1 while i <= n: sum = sum + i i = i+1 # update counter # print the sum print("The sum is", sum) ``` > while 可以搭配 else > - 當條件為 False 時執行 else > - else 會執行是 while loop 沒有任何 break 發生時 > - 當 break 發生時 else 不執行 ```python= counter = 0 while counter < 3: print("Inside loop") counter = counter + 1 else: print(f"counter = {counter}") # 一路跑到終止條件,執行 else ``` #### break & continue ```python= for val in "string": if val == "i": break print(val) print("The end") ``` ```python= for val in "string": if val == "i": continue print(val) print("The end") ``` ## DataTypes ### Numbers ```python= # 10 進制轉 2, 8, 16 進制 >>> bin(100) '0b1100100' >>> oct(100) '0o144' >>> hex(100) '0x64' ``` ```python= # 2, 8, 16 進制轉 10 進制 >>> print(0b1100100) 100 >>> print(0o144) 100 >>> print(0x64) 100 >>> int('1100100', 2) 100 >>> int('144', 8) 100 >>> int('64', 16) 100 ``` ### List ![](https://i.imgur.com/achiMaP.png) ```python= # Index my_list = ['p', 'r', 'o', 'b', 'e'] print(my_list[2]) print(my_list[4]) print(my_list[-1]) n_list = ["Happy", [2, 0, 1, 5]] print(n_list[0][1]) print(n_list[1][3]) # Slice my_list = ['p','r','o','g','r','a','m','i','z'] print(my_list[2:5]) # 從 idx = 2 取到 idx = 4 (不含5) = o g r print(my_list[5:]) # 從 5 到最後 = r a m i z print(my_list[:]) # dup 一個 list , 若 y = my_list 是相同物件不同參考,不是 copy # 等同於 my_list.copy() # list methods # 'append', # 'clear', # 'copy', # 'count', # 'extend', # 'index', # 'insert', # 'pop', # 'remove', # 'reverse', # 'sort' # add, change, del odd = [2, 4, 6, 8] odd[1:4] = [3, 5, 7] # 相當於把 4, 6, 8 (4-1=3)洗掉,在塞 3, 5, 7 result: 2, 3, 5, 7 print(odd) odd[:-1] = [100] # 留最後一個全清掉塞 100, result: 100, 7 odd[:] = [99] # 清空整個 list 塞 99 result: 99 print(odd) odd.append('ok') # append 一個元素在最後面 print(odd) # result: [99, 'ok'] odd.extend([1,2,3,4]) # 等同於 += print(odd) # result: [99, 'ok', 1, 2, 3, 4] odd += [5,8] # 等同於 extend print(odd) # result: [99, 'ok', 1, 2, 3, 4, 5, 8] odd.insert(1, 98) # 98 insert at index 1 print(odd) # result: [99, 98, 'ok', 1, 2, 3, 4, 5, 8] odd[2:2] = [97] # 等同於 .insert(2, 97) print(odd) # result: [99, 98, 97, 'ok', 1, 2, 3, 4, 5, 8] del odd[3] # delete 3 print(odd) del odd[:3] # delete 0 ~ 2 print(odd) odd.append(8) print(odd) # result: [1, 2, 3, 4, 5, 8, 8] odd.remove(8) # 刪除第一個出現的 8 print(odd) # result: [1, 2, 3, 4, 5, 8] # 如果要刪除某個在 list 出現多次的 item 的話,用 del, remove 都不行,因為 index 會變 # 當第一次刪除時,後面的元素 index 會向前補 # 所以只能創造另外一個 list 把要刪除的元件排除 odd.append(8) odd.append(8) odd.append(8) odd.append(8) new_odd_without8 = [i for i in odd if i != 8] print(new_odd_without8) print(odd) # result: [1, 2, 3, 4, 5, 8, 8, 8, 8, 8] print(odd.pop(), odd) # result: 8 [1, 2, 3, 4, 5, 8, 8, 8, 8] print(odd.pop(), odd) # result: 8 [1, 2, 3, 4, 5, 8, 8, 8] print(odd.pop(), odd) # result: 8 [1, 2, 3, 4, 5, 8, 8] 從最後面一個 pop 出來 print(odd.pop(4), odd) # result: 5 [1, 2, 3, 4, 8, 8] 從 index 4 pop 一個元素 odd[4:6] = [] # 透過 assign 來刪除元素 print(odd) # result: [1, 2, 3, 4] odd = [99, 4] + odd print(odd) # result: [99, 4, 1, 2, 3, 4] print(odd.index(3), odd) # result: 4 [99, 4, 1, 2, 3, 4] 元素 3 在什麼位置 print(odd.count(4), odd) # result: 2 [99, 4, 1, 2, 3, 4] 元素 4 有幾個 odd.sort() # 對 list 元件 sort() 和使用 sorted(odd) 不同,sorted 不會改變原 list print(odd) # result: [1, 2, 3, 4, 4, 99] odd.reverse() # 對 list 元件 reverse element 和使用 reversed(odd) 不同,reversed 不會改變原 list print(odd) # result: [99, 4, 4, 3, 2, 1] # exist print(99 in odd) # result: True print(99 not in odd) # result: False ``` #### List comperhension ```python= str_human = 'human' # [expression for item in list] h_letter = [ch for ch in str_human] print(h_letter) # ['h', 'u', 'm', 'a', 'n'] n_list = [x for x in range(20) if x % 2 == 0] print(n_list) # if with list comperhension y_list= [y for y in range(100) if y % 2 == 0 and y % 5 == 0] print(y_list) # if...else With List Comprehension even_list = ["even" if i % 2 == 0 else "odd" for i in range(10)] print(even_list) matrix = [[1,2], [3,4], [5,6], [7,8]] # by for new_matrix = [] for i in range(2): tmp_l = [] for row in matrix: tmp_l.append(row[i]) new_matrix.append(tmp_l) print(new_matrix) print([row[i] for i in range(2) for row in matrix]) # [1, 3, 5, 7, 2, 4, 6, 8] 無法分成兩組 # 採用另外一種寫法 [ [i for i in item] for item in list ] # by nested comperhension new_matrix_comp = [[row[i] for row in matrix] for i in range(2)] # 將 nested group 元素,當成一組傳回去 print(new_matrix_comp) # [[1, 3, 5, 7], [2, 4, 6, 8]] matrix2 = [[1,2,3,4], [5,6,7,8]] new_matrix_comp2 = [[row[i] for row in matrix2] for i in range(4)] print(new_matrix_comp2) # [[1, 5], [2, 6], [3, 7], [4, 8]] ``` ```python= # 建立一個固定長度的 list 並切始化所有元素 >>> [1] * 10 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ``` ### Tuple > - 在 indexing, Negative indexing, slicing 都和 List 一樣 > - List功能強大,它內容可以改變,同時也可新增和刪除,但Tuple資料不可修改。 > - Tuple執行速度比List快,因為其內容不會改變,因此Tuple內部結構比List簡單,執行速度較快。 > - 存於Tuple的資料較為安全,因為內容不會改變,不會因程式設計疏忽而變更資料內容 > 需特別注意只有單一元素的 tuple 的定義 > ```python= > my_tuple = ("hello") > print(type(my_tuple)) # <class 'str'> > > my_tuple = ("hello",) # 要建立一個 element 的 tuple 的寫法 > print(type(my_tuple)) # <class 'tuple'> > ``` ```python= my_tuple = 3, 4.6, "dog" # create tuple print(my_tuple) # result: (3, 4.6, 'dog') a, b, c = my_tuple # unpacking tuple print(a, b, c) type_int = 10 print(type(type_int)) # result: <class 'int'> type_tuple = 10, # 不用刮號也可以建立 tuple print(type(type_tuple)) # result: <class 'tuple'> my_tuple = ("mouse", [8, 4, 6], (1, 2, 3)) print(my_tuple[1][:-1]) # result: [8, 4] my_tuple[1][0:1] = [] # 把元素砍掉 print(my_tuple) # result: ('mouse', [4, 6], (1, 2, 3)) a_tuple = 10, 2 b_tuple = 3, print((a_tuple + b_tuple)*2) # result: (10, 2, 3, 10, 2, 3) 將兩個 tuple 組合在重複2次 # Tuple method # 'count' # 'index' my_tuple = ('a', 'p', 'p', 'l', 'e') print(my_tuple.count('p')) # result: 2 有幾個 p print(my_tuple.index('p')) # result: 1 第一個 p 在那個 idx # exist print('a' in my_tuple) # result: True print('c' not in my_tuple) # result: True # tuple comperhension # 因為 () 的縮寫被 generator 用了,所以 tuple 的 comperhension 需要用 tuple() 來做 print(tuple(i for i in my_tuple if i != 'p')) ``` ### String ```python= str = 'programe' print(str[0]) # result: p print(str[-1]) # result: e print(str[1:5]) # result: rogr try: str[0] = 'd' # string are immutable (TypeError: 'str' object does not support item assignment) except Exception as e: print(e) # + concat str1 = 'hello' str2 = 'world' print(str1 + str2) # * repeat print((str1+' ') * 3) # iterating l_counter = 0 for i in str1+str2: if i == 'l': l_counter +=1 print(l_counter) # or using comperhension l_counter = len([i for i in str1+str2 if i == 'l']) print(l_counter) # exist print('w' in str2) # result: True print('w' in str1) # result: False for idx, ch in enumerate(str1, 1): print(idx, ch) # 1 h # 2 e # 3 l # 4 l # 5 o ``` #### format ![](https://i.imgur.com/qog1V0j.png) ![](https://i.imgur.com/CJzaZuA.png) > - Type > - `d` Decimal > - `b` Binary > - `x` Hex > - `f` float > - Alignment > - `<` 靠左 > - `^` 置中 > - `>` 靠右 > - `=` 強制 (+)(-) 符號位置 ```python= a_str = "{}, {} and {}".format('John', 'Bill', 'Sean') print(a_str) # John, Bill and Sean a_str = "{2}, {0} and {1}".format('John', 'Bill', 'Sean') print(a_str) # Sean, John and Bill a_str = "{x}, {y} and {z}".format(z='John', x='Bill', y='Sean') print(a_str) # Bill, Sean and John a_str = "{} and {z}".format('John', z='Bill') print(a_str) # John and Bill print("The number is:{:d}".format(123)) print("The float number is:{:f}".format(123.4567898)) # The float number is:123.456790 (預設 6 位小數) print("The float number is:{:.3f}".format(123.4567898)) # The float number is:123.456 (3位小數) print("bin: {0:b}, oct: {0:o}, hex: {0:x}".format(12)) # bin: 1100, oct: 14, hex: a (都用第一欄的資料) # 轉二進制 print('{:b}'.format(10)) print(bin(10)[2:]) # 長度為 10 的欄位 print('{:10} good'.format('ok')) # 長度和小數位數一起使用 print("{:5d}".format(12)) # 5 位右邊對齊 result: " 12" print("{:2d}".format(1234)) # 這個沒用,位數超過 result: "1234" print("{:8.3f}".format(12.2346)) # 整數加小數共8位,向右對齊 result: " 12.235" (3位四捨五入) print("{:05d}".format(12)) # 不足5位數,補0 result: "00012" print("{:08.3f}".format(12.2346)) #補0、小數3位、總長度8 result: "0012.235" # show the + sign print("{:+8.3f} {:+8.3f}".format(12.23, -12.23)) # result: " +12.230 -12.230" print('-'*50) print("\"{:^10.3f}\"".format(12.2346)) # result: " 12.235 " 置中對齊 print("\"{:<10d}\"".format(12)) # result: "12 " 靠左對齊 print("\"{:=15.3f}\"".format(-12.2346)) # result: "- 12.235" (符號對齊) print("\"{:*^15d}\"".format(12)) # padding with '*' result: "000000120000000" # unpack dict and format person = {'age': 23, 'name': 'Adam'} print("{name}'s age is: {age}".format(**person)) # Adam's age is: 23 ``` ```python= # string method str1 = "PrOgRaMiZ" print(str1.upper()) # 全大寫 print(str1.lower()) # 全小寫 str2 = "hello worlld" print(str2.split()) # ['hello', 'world'] 切割變 list list1 = ['a', 'b', 'c'] print(','.join(list1)) # a,b,c 把 list join 成 string print(str2.find('ll')) # 找出第一個 ll substring 的 index print(str2.rfind('ll')) # 找出最後一個 ll substring 的 index str2 = str2.replace('ll', 'LL') # 把 string 中所有出現的 ll 換成 LL print(str2) # heLLo worLLd ``` ```python= # 取得 list 中字串長度最短的字串 >>> min(['flower', 'flow', 'flight'], key=len) 'flow' # 取得 list 中字串長度最長的字串 >>> max(['flower', 'flow', 'flight'], key=len) 'flower' ``` ### Set ```python= my_set = {1,2,3, (1,2,3), (1,2,3), "hello"} print(my_set) l_list = [1,2,3,4,5,5,8,8,7] l_set = set(l_list) # 從 list 轉 set 把 dup 部份處理掉 print(l_set) try: mutable_set = {[1,2,3]} # set 不能有可改變的元素 except Exception as e: print(e) a = {} # 這個是建立 dict 不是 set a = set() # 要用 set() 來建立 empty set my_set.add(100) # 加一個元素 print(my_set) my_set.update([100, 99]) # 加一組元素 print(my_set) my_set.discard(99999) # 丟掉一個叫 99999 的元素,不存在沒關係 try: my_set.remove(99999) # 砍掉一個叫 99999 的元素,不存在會噴錯 except Exception as e: print(e) print(my_set) for i in range(len(my_set)): print(my_set.pop()) # 一次噴一個出來,順序不一定 ``` ```python= A = {1,2,3,4,5} B = {4,5,6,7,8} # Union 連集 print(A | B) # Intersection 交集 print(A & B) # Difference 只有 A 的,就是減去 B print(A - B) # Symmetric Difference A 和 B 連集,不要共有部份 print(A ^ B) print((A | B) - (A & B)) ``` ```python= apple_set = set("apple") # 等同於 {"a", "p", "l", "e"} print(apple_set) apple_set2 = {"apple"} # 不同於 {"a", "p", "l", "e"} print(apple_set2) print('a' in apple_set) # True print('d' in apple_set) # False ``` ### Dict > 鍵值必須為不可改變的類型 (string, number, tuple) 且必須是唯一的 > 用 `[]` 來 indexing 當 key 不存在時會噴 exception ,而使用 `.get()` 則可以避免 exception 當 key 不存在時會回傳 None ```python= # create dict my_dict = {'name': 'John', 1: [2,3,4]} # key-value 可以是不同型別 # access the value by key print(my_dict['name']) try: print(my_dict['hello']) except: print('key not exist') print(my_dict.get('hello')) # result: None # add new piar or update value my_dict['hello'] = 'world' my_dict['name'] = 'Ken' print(my_dict) # removing elements print(my_dict.pop('name')) # 移除 'name': 'Ken' 這組 key-value 回傳 value print(my_dict.popitem()) # 隨便移除 dict 中的一組 key-value my_dict.clear() # 清空整個 dict print(my_dict) # result: {} # create dict from list and fill value words = ['a','e','i','o','u'] dict_words = dict.fromkeys(words, True) for key, value in dict_words.items(): # return (key, value) tuple print(f"{key} => {value}") for key in dict_words.keys(): print(key) for value in dict_words.values(): print(value) # update dict by the other dict (merge) dict_words.update({'hello': 'world', 'a': False}) print(dict_words) # check key exist squares = {1: 1, 3: 9, 5: 25, 7: 49, 9: 81} print(10 in squares) # result: False print(1 in squares) # result: True # count elements of dict print(len(squares)) ``` #### Dict comperhension ```python= squires = {i: i*i for i in range(10)} print(squires) # combine with if even_squires = {i: i*i for i in range(10) if i % 2 == 0} print(even_squires) test_dict = {'jack': 38, 'michael': 48, 'guido': 57, 'john': 33} new_dict = {k: v for k, v in test_dict.items() if v > 40 and v < 50} print(new_dict) # combine with if... else... test_dict = {'jack': 38, 'michael': 48, 'guido': 57, 'john': 33} new_dict = {k: 'old' if v > 40 else 'young' for k, v in test_dict.items()} print(new_dict) # nest d = {} for x in range(1, 10): d[x] = {y: x*y for y in range(1, 10)} # 9x9 print(d[5][8]) # 8*5 = 40 ``` ### ListNode > python 沒有內建 link-list 因此可以透過簡單定義 Node Class 來自行達成實作 > > ```python= > class ListNode: > def __init__(self, val=0, next=None): > self.val = val > self.next = next > > head = ListNode(1) > head.next = ListNode(2) > ``` ```python= from ListNode import ListNode sample_list = [1, 2, 5, 3, 6, 8] # 走訪整個 link-list def show_link_list(l): tail = l while tail: print(tail.val, end='->') tail = tail.next else: print() # 換行 # create a link-list node from list head = ListNode(sample_list[0]) tail = head # 初始,頭尾都指向同一個 for i in range(1, len(sample_list)): next_node = ListNode(sample_list[i]) tail.next = next_node # 最舊的指向新建的 tail = next_node # 指向最後一個 show_link_list(head) # link-list to list tail = head to_list = [] while tail: to_list.append(tail.val) tail = tail.next # link-list to reverse int # 將 link 一個一個走訪,每個乘 10 倍數 ... 1, 10, 100, 1000 multi = 1 tail = head result = 0 while tail: result += tail.val * multi multi *= 10 tail = tail.next print(result) # int to link-list # 透過 int 先轉 str 一格一格建立 link-list sample_i = 998796 sample_i_str = str(sample_i) head = ListNode(sample_i_str[0]) tail = head for idx in range(1, len(sample_i_str)): next_node = ListNode(sample_i_str[idx]) tail.next = next_node tail = next_node show_link_list(head) # result: 9->9->8->7->9->6-> # int reverse link-list # 透過 int 除以 10 餘數取出一個一個餘數來建立 link-list sample_i = 998796 head = ListNode(sample_i % 10) sample_i //= 10 tail = head while sample_i > 0: next_node = ListNode(sample_i % 10) sample_i //= 10 tail.next = next_node tail = next_node show_link_list(head) # result: 6->9->7->8->9->9-> ``` >Link-list 的反轉 (reverse) 行為圖 ![](https://media.geeksforgeeks.org/wp-content/cdn-uploads/RGIF2.gif) ```python= # reverse link-list prev = None curr = head while curr: next = curr.next # 先記錄下一個,它要被換掉了 curr.next = prev # 把下一個指向 prev prev = curr # prev 變成現在這個,準備給下個節點指向 curr = next # curr 可以指到下個了,當還有沒有節的條件 head = prev # curr 是 None 了,本來最後的節點變成是 prev 了 show_link_list(head) # result: 9->9->8->7->9->6-> ``` ```python= #Link-list 的合併 (一個 Loop 操作兩個 Link-list) def mergeTwoLists(list1: Optional[ListNode], list2: Optional[ListNode]): head = None if list1 is not None and list2 is not None: # 若都不是 None 就挑個小的當 head if list1.val < list2.val: head = ListNode(list1.val) list1 = list1.next else: head = ListNode(list2.val) list2 = list2.next elif list1 is None and list2: # 其中一個是 None 那結果就是另一個 return list2 elif list2 is None and list1: return list1 tail = head while list1 and list2: # 只要兩個 link-list 都還有 node 跑到一邊沒有為止 if list1.val < list2.val: tail.next = ListNode(list1.val) list1 = list1.next else: tail.next = ListNode(list2.val) list2 = list2.next tail = tail.next if list1: # 若list1 還有 node 直接接過去要 merge 的尾 tail.next = list1 if list2: # 若list2 還有 node 直接接過去要 merge 的尾 tail.next = list2 return head ``` ## Object-Oriented Programming ### Classes and Objects > - class object > - ex: class Person > - instance object > - ex: p = Person() > - function object > - ex: Person.greet > - method object > - ex: p.greet ```python= class Person: """this is person class""" pass print(Person.__doc__) # result: this is person class ``` > `self` 不是關鍵字,在 OOP 中它只是個佔位符,但傳統上還是使用 `self` 這個字 > obj.method() == class.function(obj) ```python= class Person: age = 10 def greet(self): print("hello") p = Person() print(Person) # <class '__main__.Person'> 在 main 下的 Person class object print(Person.greet) # <function Person.greet at 0x10298a310> 將 class 視為 namespace 下使用 function 但必須傳入第一個參數為 Person 的物件實例 print(Person.age) # class attribute print(p.greet) # <bound method Person.greet of <__main__.Person object at 0x10298efd0>> 實列物件已經挷定到類別物件函式 p.greet() # 等同於 Person.greet(p) Person.greet(p) # 等同於 p.greet() ``` > python 的 garbage collection 是將 reference 到該 object 的 variable 砍掉,而在 memory 中的 object 沒有被 bind ,所以 garbage collection 機制自動去清掉它 > > ```python= > h = Hello() # create a Hello object and use h to bind it. > del h # delete the bind relation between h and Hello object > # after that, Hello object in memory will be destroyed by garbage collection > ``` > 存取 object attribute 不存在時,會 reference 到 class attribute 但是當改變不存在的 object attribute 時,則會 **建立** 新的 object attribute ```python= class Person: type = "citizen" def __init__(self, age: int): self.age = age def get_age(self): return self.age print(Person.type) # result: citizen p1 = Person(30) p2 = Person(27) print(p1.type, p2.type) # result: citizen citizen Person.type = "not citizen" print(p1.type, p2.type) # result: not citizen not citizen (object attribute not exist, reference to class attribute) p1.type = "p1 citizen" # 建立了 attribute of object print(p1.type, p2.type) # result: p1 citizen not citizen ``` ### Encapsulation ```python= class Account: def __init__(self): self.passwd = '0000' def get_password(self): return self.passwd def set_password(self, new_pwd): self.passwd = new_pwd class NewAccount: '''better practice to encapsule''' def __init__(self): self.passwd = '0000' @property def password(self): return self.passwd @password.setter def password(self, new_pwd): self.passwd = new_pwd def set_password(self, new_pwd): self.password = new_pwd a = Account() print(a.get_password()) a.set_password('1234') print(a.get_password()) new_a = NewAccount() print(new_a.password) new_a.password = '5678' # setter print(new_a.password) new_a.set_password('9999') # call set function print(new_a.password) ``` ### Inheritance > ```python= > Employee.__init__(self, name, year) # equal below > super().__init__(name, year) # this is good practice > ``` ```python= class Employee: def __init__(self, name, year): self._name = name self._year = year def salary(self): base = 100 for i in range(self._year): base = base * 1.03 return base class Engineer(Employee): def __init__(self, name, year): # Employee.__init__(self, name, year) super().__init__(name, year) def get_detail(self): return f"name: {self._name} salary: {self.salary()}" def salary(self): # override return super().salary() * 1.1 # call parent salary() andy = Engineer('Andy', 10) print(andy.get_detail()) ``` ### Class method and Static method > Class Method > - 和 class variable 一樣用在 class 本身,而不是產生的物件上 > - 像想知道該 class 產生多少物件,就適點用 class method and class variable > - 可做為建構子的替代,透過不同的參數來產生物件 > - storage.from_connection_string() 建構 storage object > ```python= > @classmethod > def method_name(cls): > # cls is the class reference > # cls() == Class() > ``` > Static Method > - 想建立一個方法,但不牽涉物件或類別本身,就是不會改變物件,或類別狀態 > - 常用來建立類別的工具集 (以外部的角度來使用該物件的操作) > - Car.validate(car_object) 一個靜態方法操作物件 > ```python= > @staticmethod > def method_name(parameter1): > # only deal with the parameter1 > # don't have any relationshiop with class or object > ``` > Class Method vs Static Method > | method | class | static | > |---|---|---| > | 參數 | cls | 沒有 | > | 存取狀態 | 類別狀態 | 無法存取狀態 | > | 標記方式 | @classmethod | @staticmethod | > | 用途 | 適用於 factory pattern | 適用以外部角度操作物件 | > **NameError: name 'Class' is not defined** > 如果有用到 typing hint 的話,在 class 中要 reference 自己的類別時,會發生這個錯誤。這是因為該「類別物件」還沒與「class name」挷定 > > - 3.7 以下可以使用 > ```python= > def is_vw_car(car: 'Car') -> bool: # 改為 string 來解決 > ``` > - 3.7 以上可以加入以下 annotations 來解決 > ```python= > from __future__ import annotations > ``` > - 3.10 就會自動解決這個問題了 ```python= class Car: _car_counter = 0 # class variable def __init__(self, brand): self._brand = brand Car._car_counter += 1 @classmethod def vw_brand(cls): return cls('Volkswagen') # return Car object @classmethod def get_car_count(cls) -> int: return cls._car_counter def get_brand(self) -> str: return self._brand h = Car('Honda') vw = Car.vw_brand() for i in [h, vw]: print(i.get_brand()) print(Car.get_car_count()) # result: 2 ``` ```python= from __future__ import annotations class Car: def __init__(self, brand): self._brand = brand def get_brand(self) -> str: return self._brand @classmethod def vw_brand(cls): return cls('Volkswagen') # return Car object @staticmethod def is_vw_car(car: Car) -> bool: # check objct is vw return car.get_brand() == 'Volkswagen' h = Car('Honda') vw = Car.vw_brand() print(Car.is_vw_car(h)) print(Car.is_vw_car(vw)) ``` ### Class and Object create flow > - 所有定義的 class 都會自動繼承 object class 因此實例的物件都具有 object 基本功能 > - \_\_init__ > - \_\_new__ > - \_\_str__ > - ... > - 在 python 中所有的東西都是物件 > - a = 9 表示 9 是 class int 的實例化,並透過 a 來 reference 它 > - object or class 都可以使用 type() 來確認它的 class 是那個 > - type(9) <class 'int'> > - type(int) <class 'type'> > - type(type) <class 'type'> > - type(h) 等同於 h.\_\_class__ > - type class (MetaClass) > - 在 python 世界中,所有建立的 class 都是 type class 的物件 (它的實例化) 所以 type 又稱 metaclass > > - class, object, metaclass 的關係 > ![](https://i.imgur.com/mks5G8k.png) > - Human class 是從 Metaclass type class 生出的 object > - 這個 Object 是個 class 並繼承了 object class 因此具有 \_\_init__, \_\_new__, \_\_str__ 等 methods > - 透過 class 這個物件,再實例化物件就是 human_obj > - human_obj 同時也有 object 的所有 methods ```python= class Human: pass # Human 繼承 object 它是 class 是 type print(dir(Human)) ''' ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] ''' # __bases__ 可以列出該 Class 的繼承列表 print(Human.__bases__) # object 提供許多 method 如 __new__, __init__, __str__ 這些都會被定義的 class 所繼承 print(dir(object)) ''' ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] ''' # 9 是物件,而 a 是這個物件的 reference a = 9 print(type(a)) # <class 'int'> print(a.__class__) # <class 'int'> 同上 # 建立 Human 物件,而 h 是這個物件的 reference h = Human() print(type(h)) # <class '__main__.Human'> # 用來建立物件的 class 它本身自己也是物件,是 class type 的物件 # class type 用來建立所有新的類別(class)的類別,它稱為 Metaclass print(type(Human)) # <class 'type'> print(type(int)) # <class 'type'> print(type(float)) # <class 'type'> print(type(type)) # <class 'type'> print(type(object)) # <class 'type'> def simple_function(): pass print(type(simple_function)) # <class 'function'> class Car: color = 'blue' def __init__(self, color): self.color = color c1 = Car('red') print(c1.color) # 物件建立流程等同於以下行為 c2 = object.__new__(Car) # step1. create object print(c2.color) # result: blue (當 object variable 不存在,會 reference class variable) Car.__init__(c2, 'yellow') # step2. init object print(c2.color) # result: yellow h = type.__call__(Human) # 等同於 h = Human() print(h) ``` > 物件產生問題 > - \_\_init__ 沒有回傳任何的東西,物件怎麼產生的 > - \_\_init__ 中的第一個參數 self 怎麼傳進來的 > > 物件產生流程 > - step1. > - 建立物件 > - 使用 \_\_new__ method 來建立物件 > - 如果 class 沒有定義 \_\_new__ 則會使用繼承的 object.\_\_new__ > - \_\_new__ > - is a static method 可以直接 object.\_\_new__(cls) 呼叫 > - cls 為「類別物件」由 metaclass 產生出來的類別物件 > - 它將為 cls 類別配置記憶體,並建立/回傳該物件 > - 會使用 super().\_\_new__(cls) 來呼叫 > - 不會做任何物件初始化動作,只會建立 > - 可以透過 override \_\_new__ 來自己處理建立並初始物件的動作 > - 它不需要 @staticmethod 裝飾,它就是個 static method 是個特殊的存在 > - step2. > - 初始物件 > - 使用 \_\_init__ method 來初始物件 > - 不回傳任何資料 > - 第一個參數為該類別實例化的物件 > - \_\_init__ 進行初始化,不回傳任何東西 > - 參數接收數量 \_\_new__ 與 \_\_init__ 需要一樣 > > 透過 type.\_\_call__(cls) 串起整個流程 > - Human() 等同於 type.\_\_call__(Human) 因為 Human 這個 類別物件是由 metaclass type 生成出來的,因此它也繼承了 type.\_\_call__() 的方法 > - 當 Human() 發生了以下事情 > 1. type.\_\_call__(Human) > 2. Human.\_\_new__(Human) 建立物件回傳物件給 type.\_\_call__ > 1. obj = object.\_\_new__(Human) > 2. return obj > 3. Human.\_\_init__(obj, parameters) 呼叫 init 做初始化 > 4. obj .name = name 進行初始 > 5. type.\_\_call__(Human) 結束回傳 obj > ![](https://i.imgur.com/tLL7ieP.png) ```python= # 定義建立物件時同時初始資料,因此不用實作 __init__ class Human: def __new__(cls, name): # cls = "Human" obj = super().__new__(cls) # create obj obj.name = name return obj # 必須要回傳,否則就沒有 reference 了 h = Human('ken') print(h) # <__main__.Human object at 0x103448ca0> print(h.name) # ken # 也可以這樣建立 h = object.__new__(Human) # 土炮建立物件 h.name = 'John' # 土炮初始物件的 attributes print(h) print(h.name) # John ``` ```python= # 標準做法定義 __new__, __init__ 讓 type.__call__ 來啟動整個流程 class Human: def __new__(cls, *args, **kwargs): print(f"new method: {args} {kwargs}") obj = super().__new__(cls) return obj def __init__(self, first_name, last_name): print(f"init method: {first_name} {last_name}") self.first_name = first_name self.last_name = last_name def __str__(self): return f"{self.first_name} {self.last_name}" h = Human('John', 'cena') print(h) h2 = type.__call__(Human, 'Ken', 'chang') print(h2) ``` ```python= # 類別實作 __call__ 的方法,讓物可以 callable class Human: def __init__(self, name): self.name = name def __call__(self, *args, **kwargs): print(f"{self.name} {args}") h = Human('ken') h('ok') h.__call__('ok') # same above Human.__call__(h, 'ok') # same above ``` ```python= # 透過 __new__ 實現 singleton class Human: __inst = None def __new__(cls, *args, **kwargs): if cls.__inst is None: cls.__inst = super().__new__(cls) return cls.__inst def __init__(self, name): self.name = name def __str__(self): return f"{id(self)} {self.name}" h = Human('Ken') print(h) # 4336594704 ken h2 = Human('John') print(h) # 4336594704 John ``` ## Keywords ### Function > 在 python 中定義 function 必須早於呼叫 > 沒有任何的 return value 時 caller 會拿到 None ```python= def greet(name): # one argument """ This function greets to the person passed in as a parameter """ print("Hello, " + name + ". Good morning!") greet('Paul') print(greet.__doc__) # 印出說 function 的說明 ``` ```python= def greet(name, msg="Good morning!"): # default argument # default argument 必須在 non-default 的後面 print("Hello", name + ', ' + msg) greet("Kate") greet("Bruce", "How do you do?") # 2 keyword arguments greet(name = "Bruce",msg = "How do you do?") # 2 keyword arguments (out of order) greet(msg = "How do you do?",name = "Bruce") # 1 positional, 1 keyword argument # 當 default argument 很多時,這個較方便使用, 直接指定 non-default arguments greet("Bruce", msg = "How do you do?") ``` > keyword arguments must follow positional arguments. > > 參數的順序 > 1. positional arguments ex: name > 2. arbitraty arguments ex: *name > 3. default arguments ex: name='John' > 4. keyword arguments ex: **name ```python= # abrbitrary argument def show_name(*names): print(type(names)) print(names) for name in names: print(name) show_name('dachi', 'chris', 'darren') # keyward argument def show_name2(prefix='@', **names): print(type(names)) for k, v in names.items(): print(f"{prefix}{k} {v}") show_name2(prefix='hello ', dachi='chang', lumi='chang') # hello dachi chang # positional, arbitrary, default, keyword arguments def show_name3(prefix, *dect, suffix='.', **names): for d in dect: print(f"{d} {prefix}: {names['first']}-{names['last']} {suffix}") show_name3('Hi', '@@', '%%', first='dachi', last='chang', suffix='...') # @@ Hi: dachi-chang ... ``` ### Global > - 在該 module 下所有的 variable, class, constants, function 都是 global > - 當 import 別的 module 到目前的 namespace 時,必須以 MODULE.VARIABLE 存取該變數 > - ex: config.a > - ex: config.b > - 當不具 namespace 進行 import 時,該變數可能被現在的 namespace global variable 蓋掉 > - ex: from config import a, a = 10 此時 a 就被蓋掉了 ```python= def outer_function(): global a # reference module global a a = 20 def inner_function(): global a # reference module global a a = 30 print('a =', a) inner_function() print('a =', a) a = 10 outer_function() print('a =', a) ``` ### Iterator and Generator > - Iterable > - 可執行 Iteration 的 objects 都稱為 Iterable(可當專有名詞)。參照 官方文件 提及,是指可以被 for loop 遍歷的 objects。以程式碼來說,只要具有 `__iter__` 或 `__getitem__` 的 objects 就是 Iterable。 > - 像是 list, range 是 Iterable > - Iterator > - 遵照 Python Iterator Protocol 的 objects。以 Python3 而言,參照 官方文件,只要具有 `__iter__` 和 `__next__` 的 objects 皆為 Iterator。 > - Iterator 是 Iterable 的 subset。 > - 當 Iterable 呼叫了 `__iter__()` 回傳的 object 為 Iterator > - 像 map() 就是回傳 Iterator > - Generator > - 一個 routine 可以控制迴圈的迭代行為,它就像是個 function 可以回傳值 > - 出現在 python3 > - Generator 可產生一個 Iterator 物件 > - 當一個 function 中帶有 yield 時,該 function 就會自動 return 一個 Gerenator 的 object,而這 generator 自帶 `__next__` ,是一個 Iterator。 ```python= # iterator 和 generator 的關係 import sys x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # bounded, in memory, iterator object print(sys.getsizeof(x)) # result: 152 print(sys.getsizeof(range(1, 11))) # result: 48 for element in x: # iterate in iterator object print(element) for i in range(1, 11): # generator print(i) y = map(lambda i: i**2, x) # generator u = list(y) # will iterate x and create a result list, generator STOP # u = [i for i in x] print(u) # result: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] for i in y: # 直接結束,因為 generator 已終止 print(f"i in y: {i}") for i in u: # 迭代已建立的 list object ,佔用記憶體空間,可以重複使用 print(f"i in u: {i}") # 印出結果 ``` ```python= # iterator 如何運作 x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] y = map(lambda i: i**2, x) print(next(y)) # next() 用在 iterator object 以取得回傳值 print(y.__next__()) # same above print(next(y)) print(next(y)) for i in y: # 只是幫忙 call next() 而己 print(f"use for to print: {i}") # 等同於以下 while loop # while True: # try: # value = next(y) # print(value) # except StopIteration: # print('Done') # break ``` ```python= # range generator x = range(1,11) # next(x) x is not iterator object y = iter(x) print(type(x)) # <class 'range'> print(type(y)) # <class 'range_iterator'> Iteratable Generator Object print(next(y)) # 先取得 iterator object 再 next() result: 1 for i in y: print(i) # result 2 ~ 10 try: print(next(y)) except StopIteration: print("no more value") print('-'*50) for i in x: # equal for i in iter(x) on the fly, 每次都產生一次 generator object print(i) for i in x: # 因為不同的 generator object 所以每次使用都是新的 print(i) # 可以說 y 是產了一次 generator object 被重複使用 # x 是設計,但是未實例化 generator object 當 for loop 它會自動呼叫 iter(x) 讓它實例化成 generator object 但只有在該 for loop 使用 ``` ```python= # 實作 Iterator class MyIter: def __init__(self, n): self.n = n def __iter__(self): self.current = -1 return self def __next__(self): self.current += 1 if self.current >= self.n: raise StopIteration return self.current x = MyIter(5) print(next(iter(x))) # 每次呼叫都會讓 current = -1 重新設定一次 result: 0 print(next(iter(x))) # result: 0 print(next(iter(x))) # result: 0 for i in x: # equal: for i in iter(x) 這讓 current = 1 重新設定一次,所以每次 for loop 都能印 print(i) print('-'*50) class MyIter2: # 另外一種實作 def __init__(self, n): self.max = n self.index = 0 def __iter__(self): return self def __next__(self): self.index += 1 if self.index > self.max: raise StopIteration return self.index def __str__(self): return f"current index: {self.index}" a = MyIter2(5) for i in a: # 呼叫 iter(a) 的時候拿到 self print(i) # result: 1~5 print(a) for i in a: # 呼叫 iter(a) 的時候拿到 self 的 index 是 6 了 print(i) # 已經 StopIteration ``` ```python= # 透過 yield 實作 generator 產生 iterator 物件 def gen(n): for i in range(n): yield i x = gen(5) y = iter(x) print(type(x)) # <class 'generator'> print(type(y)) # <class 'generator'> print(next(x)) # result: 0 print(next(x)) # result: 1 print(next(x)) # result: 2 print(next(x)) # result: 3 for i in x: print(i) # result: 4 print('-'*50) def gen2(): yield 1 # 把 1 回傳之後,暫停執行,等待下次再呼叫 gen2() 時,從上次回傳點後再執行 print('back to yield 1') yield 10 # 回傳 10 之後,又暫停執行 print('back to yield 10') yield 100 print('back to yield 100') yield 1000 print('back to yield 1000') yield 10000 print('back to the last yield') for i in gen2(): print(i) print('-'*50) u = (i for i in range(10)) # 是個 generator 的語法糖,讓你不用寫 def 和 yield print(x) # <generator object gen at 0x100301cf0> print(u) # <generator object <genexpr> at 0x100301c80> print(next(u)) # result: 0 for i in u: print(i) # result: 1 ~ 9 ``` ### Yield > - yield 只能出現在 function 裡 > - call 帶有 yield 的 function 會回傳一個 Generator object > - Generator object 是一個 Iterator,帶有 `__iter__` 和 `__next__` attributes > - yeild 除了可傳出值外,也可以接受由外部輸入的值,利用 .send() 即可同時傳入值也傳出值,讓 Generator object 可和外部進行雙向溝通,可以傳出也可以傳入值 > - 和 return 搭配的 function,就是吃進 input 然後輸出 output,life cycle 就會消失,yield 的寫法可以視為擴充 function 的特性,使其具有「記憶性」、「延續性」的特質,可以不斷傳入 input 和輸出 output,且不殺死 function > - 第一次 call next(generator) 執行內容等價於將原 function 執行到第一次出現 yield 之處,「暫停」執行,並返回 yield 後的值 > - 第二次之後每次 call next(generator) 都會不斷從上次「暫停」處繼續執行,直到 function 全部執行完畢並 raise StopIteration ```python= # get and send to generator def my_generator(value=0): while value < 10: get_value_from_send = yield value value += 1 print(f"get value: {get_value_from_send}") gen = my_generator() print(next(gen)) # 取得 value, result: 0 print(gen.send(5)) # 對 generator 傳入值,並取得結果,相當於 send() 之後再 next(), result: 5 1 print(next(gen)) # 沒有 send(), result: get value None, 2 print(gen.send(100)) # result: get value: 100 3) print(next(gen)) # 4 print(next(gen)) # 5 print(next(gen)) # 6 print(next(gen)) # 7 print(next(gen)) # 8 print(next(gen)) # 9 try: print(next(gen)) # result: StopIteration except StopIteration: print("StopIteration") ``` ### Import > - 檔案稱為 module > - 資料夾稱為 package > - class, function, variable, object, constants 都能夠被 import 進來 > from PACKAGE.MODULE import * is not a good programming practice. ```python= >>> import math # import module >>> from math import pi # import module's only pi object >>> from variable_scope.scope import outer_function # import variable_scope package's scope module's outer_function ``` ### Package and Import > python 3.3 以後不強制要有 `__init__`.py 檔案 (from Python 3.3+ supports Implicit Namespace Packages that allows to create a package without an `__init__`.py file.) > 幾個重要的原則 > > - 避免在 Module 中放置定義以外的東西 Module 是用來被 Import 後執行的,執行的程式碼最好放在外部,否則容易發生執行上的混亂 > - 同一個 Package 中的 Module 互相引用使用 **Relative Import** > - 不同 Package 中的 Module 互相引用使用 **Absolute Import** > - 執行檔(main.py)引用模組使用 **Absolute Import** > >> 只有同一個 Package 中的 Module 要互相引用,才用 Relative Import,否則都用 Absolute Import > `__init__`.py 可以做為 package 提供對外的接口,可用於隱藏一些 sub module 的實現,有點像是 Facade 的概念 ``` Layout of package . ├── main.py ├── my_package │   ├── sub_pkg1 │   │   ├── module_a.py │   │   └── module_b.py │   └── sub_pkg2 │   └── module_c.py └── some_package ├── __init__.py └── sub_pkg1 ├── module_d.py └── module_e.py ``` ```python= # my_package/sub_pkg1/module_a.py from .module_b import bar # 使用相同 package 的另外一個 module_b 的 bar # 採 relative import def foo(): print("module a foo.", bar()) ``` ```python= # my_package/sub_pkg1/module_b.py def bar(): return "moduel b return bar." ``` ```python= # my_package/sub_pkg2/module_c.py from my_package.sub_pkg1.module_a import foo # 用到同 package 不同的 sub package 的 module # 採 absolute import def show_foo(): print("show foo()") foo() ``` ```python= # some_package/sub_pkg1/module_d.py from my_package.sub_pkg1.module_a import foo # 用到不同 root package 的 module 功能 # 採 absolute import def decorate_foo(): print(">"*20) foo() print("<"*20) ``` ```python= # some_package/sub_pkg1/module_e.py from .module_d import decorate_foo # 用到同 package 不同 module 採 relative import def decorate_foo_show(): decorate_foo() ``` ```python= # some_package/__init__.py from .sub_pkg1.module_e import decorate_foo_show # __init__ 用來 export package 的功能 # 對所有 scope 以下的都採 relative import ``` ```python= # main.py from my_package.sub_pkg2.module_c import show_foo # import function from some_package.sub_pkg1 import module_e # import module import some_package # import package __init__.py show_foo() module_e.decorate_foo_show() # 透過 module namespace 使用功能 some_package.decorate_foo_show() ``` ### Pass > - 對 python 說來就像是 no operation (NOP) 會執行,但是沒動作 > - 一般用來當 placeholder > - 用在未來將會實作,現在先 pass ```python= for val in sequence: pass def function(args): pass class Example: pass ``` ### Lambda (Anonymous Function) > lambda arguments: expression > > expression 結果回傳,可以是 int, str, bool 任型別 > > ex: double = lambda x: x*2 ```python= double = lambda x: x * 2 print(double(5)) ``` ```python= # 對 filter 來說,只要 expression 為 true 就會被當成 filter 的條件 # new_item = (x % 2 == 0) ? return x a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filter_generator = filter(lambda x: x % 2 == 0, a_list) # return a generator print(filter_generator) b_list = list(filter_generator) print(b_list) # [2, 4, 6, 8, 10] ``` ```python= # 對 map 來說每個 item 都會執行 expression 的動作 # new_item = str(x) a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] map_generator = map(lambda x: str(x), a_list) print(map_generator) b_list = list(map_generator) print(b_list) # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] ``` ### Exceptions ```python= print(dir(locals()['__builtins__'])) # 列出所有 built-in exceptions ``` > 如果 calling path 為 a() -> b() -> c() 而當 exception 從 C 發生,如果沒有 handle 的話,會依 c() -> b() -> a() 一路彈回去,如果在 a() 沒有 handle 的話,就直接停下來了 > - Try ... Else 處理沒有發生任何 exception 的狀況,在 Else 中發生 exception 要另外處理 > - Try ... Finally ```python= random_list = ['a', 0, 2] for entry in random_list: try: r = 1/int(entry) print(r) break # break if no exception occur except ValueError: print('value error') except ZeroDivisionError as e: print(e) except Exception as e: print(e.__class__) # raise exception try: a = int(input("enter a postive number: ")) if a <= 0: raise ValueError("is not a positive number!") except Exception as e: print(e) # try ... else and ... finally # case 1 without exception try: print("hello") except Exception as e: print(e) else: print("world") # 只有在沒發生 exception 才執行 finally: print("done") # 不管有沒有發生 exception 都執行 # case 2 with value error exception try: raise ValueError("value error!!") except Exception as e: print(e) # 處理 exception else: print("world") # 發生了 exception 所以不執行 finally: print("done") # 不管有沒有發生 exception 都執行 ``` ```python= # customization exception class SalaryNotInRangeError(Exception): def __init__(self, salary, message='salary is not in (5000, 15000) range'): self.salary = salary self.message = message super().__init__(self.message) def __str__(self): return f'{self.salary} -> {self.message}' salary = int(input("Enter salary amount: ")) try: if salary < 5000 or salary > 15000: raise SalaryNotInRangeError(salary) except SalaryNotInRangeError as e: print(e) # result: 100 -> salary is not in (5000, 15000) range print(e.salary) # result: 100 ``` ## Built-in Functions ### dir > dir([object]) > > 用在列出 object attributes 和 methods > - 當該 object 有 `__dir__`() 方法時,會呼叫它,且必須回傳 list > - 如果 object 沒有 `__dir__`() 方法時,會去找 `__dict__` 並以 list 列出 > - 如果 dir() 不帶 object 時,將列出該 scope 所有的 names ```python= from pprint import pprint x = dict(a=10, b=20) pprint(dir(x)) #['__class__', # '__class_getitem__', # '__contains__', # '__delattr__', # '__delitem__', # '__dir__', # '__doc__', # '__eq__', # '__format__', # '__ge__', # '__getattribute__', # '__getitem__', # '__gt__', # '__hash__', # '__init__', # '__init_subclass__', # '__ior__', # '__iter__', # '__le__', # '__len__', # '__lt__', # '__ne__', # '__new__', # '__or__', # '__reduce__', # '__reduce_ex__', # '__repr__', # '__reversed__', # '__ror__', # '__setattr__', # '__setitem__', # '__sizeof__', # '__str__', # '__subclasshook__', # 'clear', # 'copy', # 'fromkeys', # 'get', # 'items', # 'keys', # 'pop', # 'popitem', # 'setdefault', # 'update', # 'values'] ``` ### type > **type is used to find the type or class of an object.** > > type(object) #type with single parameter > type(name, bases, dict) # type with 3 parameters > > 可以列出 object 的型別,也可以建立新的型別 ```python= >>> l = [1,2] >>> type(l) # 列出 l 的型別 <class 'list'> >>> X = type('X', (), dict(a=10, b=20)) # 建立型別 >>> x = X() >>> x.__dict__ # 列出物件的 dict {} >>> X.__dict__ # 列出類別的 dict mappingproxy({'a': 10, 'b': 20, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'X' objects>, '__weakref__': <attribute '__weakref__' of 'X' objects>, '__doc__': None}) >>> type(X) # X 是個型別 <class 'type'> >>> type(x) # x 是個實例 <class '__main__.X'> ``` ### print > print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) ```python= a = 10 b = 20 c = 30 print("helloworld") print("hello", a, b) # result: 10 20 print(1, "hello", 3.4, sep='#', end='&\n') # result: #hello#3.4& print('The value of x is {} and y is {}'.format(a,b)) # result: The value of x is 10 and y is 20 print('Hello {a}, {b}'.format(a=a, b=b)) # result: Hello 10, 20 print("a = %d, b = %d" %(a, b), "c = %d" %c) # result: a = 10, b = 20 c = 30 print(f"a = {a}, b = {b}") # result: a = 10, b = 20 ``` ```python= # raw string print(r"This is \x61 \ngood example") #This is \x61 \ngood example ``` ### input > input([prompt]) ```python= x = input("enter a number for x: ") print(f"x = {x}") ``` ### file | 模式 | 可做操作 | 若檔案不存在 | 是否覆蓋 | |---|---|---|---| | r | 只能讀 | 報錯 | - | | r+ | 可讀可寫 | 報錯 | 是 | | w | 只能寫 | 建立 | 是 | | w+ | 可讀可寫 | 建立 | 是 | | a | 只能寫 | 建立 | 否,追加寫 | | a+ | 可讀可寫 | 建立 | 否,追加寫 | ```python= with open("test.txt", "w+") as f: f.write("Hello world\n") p1 = f.tell() f.write("How are you\n") f.seek(p1) # seek the position p1 f.write(" ok") # override the word How f.flush() # flush to file f.seek(p1) # back to position p1 result = f.readline().strip() # p1 position to end of the new line f.seek(p1) f.truncate() # truncate all data after p1 position print(result) print('-'*50) with open("test2.txt", "w+") as f: f.write("hello\n") f.write("world\n") f.write("good\n") f.write("ok\n") f.write("python\n") f.seek(0) l1 = f.readline() # read 1 line ls = f.readlines() # read all line and create list f.seek(0) data = f.read() # read entire file data to a str (including newline) print(l1.strip()) # result: hello print(ls) # result: ['world\n', 'good\n', 'ok\n', 'python\n'] print(data.strip().split("\n")) # result: ['hello', 'world', 'good', 'ok', 'python'] ``` ## Basic built-in Module ### os ```python= import os, shutil current_path = os.getcwd() print(current_path) # current path ls = os.listdir() # list the dir in current path print(ls) if 'test' not in ls: os.mkdir('test') # create a new dir which is named test os.chdir('test') # change into test dir with open('test.txt', 'w') as f: f.write('helloWorld') print(os.getcwd()) # current path os.chdir(current_path) # back to the origin current path shutil.rmtree('test') # create dir path (super mkdir) os.makedirs('a/b/c/d/e/f', exist_ok=True) shutil.rmtree('a') ``` ### json > - `.loads()` json string to dict > - `.dumps()` dict to json string > - `.load()` json file to dict > - `.dump()` dict to json file ```python= import json p = '{"name": "Bob", "languages": ["Python", "Java"]}' # load json from string dict_p = json.loads(p) print(json.dumps(dict_p, indent=4)) # dict to json string with indent=4 with open("data.json", "w") as f: json.dump(dict_p, f) # dump dict to json file with open("data.json", "r") as f: dict_p_from_file = json.load(f) # load json to dict from file print(dict_p_from_file['languages']) ``` ### math ```python= >>> math.pow(2,10) # x 的 y 次方 1024.0 >>> math.factorial(3) # 階乘 6 >>> math.pi # 常數 PI 3.141592653589793 >>> math.sqrt(9) # 平方根 3.0 >>> math.ceil(123.1) # 無條件進位 124 >>> math.floor(3.92) # 無條件捨去 3 >>> abs(-1) # 絕對值 1 >>> round(1.234) # 四捨五入 1 >>> round(1.534) 2 ``` ### random ```python= >>> random.random() # 隨機 0 ~ 1 的浮點數 0.1274161084834875 >>> random.randint(1, 10) # 隨機 1 ~ 10 整數 7 >>> random.choice('hello') # 隨機從字串中取的一字元 'l' >>> random.choice(['ok','yes','no']) # 隨機從 list 取得一個 item 'yes' >>> random.sample(['ok','yes','no'], 2) # 隨機從 list 取 2 個 item ['yes', 'ok'] >>> l = [1,2,3,4,5] >>> random.shuffle(l) # 對 l 進行 shuffle >>> l [5, 1, 3, 4, 2] ``` ```python= # 產生 a-zA-Z0-9 長度為 10 的亂數字串 >>> import random >>> import string >>> ''.join(random.sample(string.ascii_letters + string.digits, 10)) '7QKtzJB4kI' ``` ### re ### datetime > - datetime (module) > - datetime (class) `date + time` > - now() (class method) > - date (class) > - today() (class method) > - time (class) > - timedelta (class) ## Python third-party packages ### click > function name 怎麼從 options 定義中來的 > - 如果定義了一個沒有 `-` or `--` 的 name ,則優先取用 > - ex: @click.option('-t', 'to') # to 為 function 的 argument > - 如果前綴為 `--` 則 argument 為拿掉前綴的 name > - ex: @click.option('-t', '--too') # too 為 function 的 argument > - 最後是前綴為 `-` 則 argument 為拿掉前綴的 name > - ex: @click.option('-t') # t 為 function 的 argument ```python= import click from google.cloud import translate_v2 @click.group() @click.option('-t', '--target', type=str, required=True, default='zh-tw', help="iso 639-1", show_default=True) @click.option('--cert', type=str, help="path for gcp api service account json file.", required=True) def cli(target, cert): global translate_client translate_client = translate_v2.Client.from_service_account_json(cert) translate_client.target_language = target @cli.command() @click.argument('word', required=True, type=str) def translate(word): translate_format(word) @cli.command() @click.argument('file_path', required=True, type=str) def translate_by_file(file_path): try: with open(file_path, "r") as f: words = f.read().splitlines() except FileNotFoundError: print('file not exist!!') exit(1) for word in words: translate_format(word) def translate_format(word): result = translate_client.translate(word) print(f"{result['input']}\t{result['translatedText']}") if __name__ == '__main__': cli() # result: # Usage: main.py [OPTIONS] COMMAND [ARGS]... # # Options: # -t, --target TEXT iso 639-1 [default: zh-tw; required] # --cert TEXT path for gcp api service account json file. [required] # --help Show this message and exit. # # Commands: # translate # translate-by-file ``` ### requests ## Tools ### autopep8 ## Document ## Topic Document - [Pipenv HowTo](https://hackmd.io/@yLc3McFtQvyjq58xjHT7SQ/rygHU8mOK) - [Built-in functions reference](https://www.programiz.com/python-programming/methods/built-in) - [Understanding object instantiation and metaclasses](https://www.honeybadger.io/blog/python-instantiation-metaclass/) - [The metaclass in python](https://dboyliao.medium.com/%E6%B7%BA%E8%AB%87-python-metaclass-dfacf24d6dd5)