# 20240729 Python 今日內容: - is vs == (是否 vs 比較) - Tuple (元組) - Set (集合) - Expression vs Statement (表達式 vs 陳述句) - Lambda Expression (Lambda表達式 類似匿名函數) - Closure (閉包) - Decorator (裝飾器) - Generaotor (產生器) - Recursive (遞迴) - Module (模組) ## is vs == - is : 用來比較是不是同一個物件 - 可以看成雙胞胎,看起來很像但是是不同的一個人,代表不同的個體 - == : 用來比較兩個值是否相等 [情境一] ```py a = [1, 2, 3] b = [1, 2, 3] print(id(a)) # 2831169804544 print(id(b)) # 2831169802624 print(a is b) # False 因為指向不同的記憶體空間 b = a ``` ![image](https://hackmd.io/_uploads/rk2wp1SF0.png) ![image](https://hackmd.io/_uploads/HJi1pyrtA.png) ```py a = [1, 2, 3] b = [1, 2, 3] b = a # 讓a和b指向同一個地方 print(id(a)) # 2047508928768 print(id(b)) # 2047508928768 print(a is b) # True ``` ![image](https://hackmd.io/_uploads/SJbQRyrKA.png) ## Tuple vs List (元組 vs 串列) - Tuple 異質性 => (1, "a", True) - Tuple表示方式 ```py a = (1, 2, 3) ``` ![image](https://hackmd.io/_uploads/ryxckxBYR.png) ```py a = 1, 2, 3 ``` ![image](https://hackmd.io/_uploads/rJFSklHFC.png) ```py a = 1, # 單一個一定要在最後面加上逗號,才能組合成一Tuple ``` ![image](https://hackmd.io/_uploads/S1NhkeBY0.png) - List 同質性 => [1, 2, 3, 4] ## Set (集合) - 可以放`多種資料型態` - 排列`沒有順序` - 序列中的元素`不會重複` - 沒有辦法透過索引值取得資料,因為沒有索引值 - 可以透過list轉成串列後,再取資料 - 空集合寫法 ```py aa = set() ``` ![image](https://hackmd.io/_uploads/BkoWzxrtA.png) ## dict (字典) - 字典表示方式 ```py cat = {"name": "kitty", "age": 18} print(cat) print(type(cat)) ``` ![image](https://hackmd.io/_uploads/SkJ8mgrFC.png) ![image](https://hackmd.io/_uploads/HJhTMerKC.png) - 雜湊表 - 可以透過座標位置從表格中找到key和value - 使用字典,其實底下會去找雜湊表。那為什麼會取名字典,是因為Python本身就有內建雜湊函數hash() ![image](https://hackmd.io/_uploads/HyJ9VxHYR.png) - hash( )的 key 設定注意事項 - key一定要是不可變動的,例如:Tuple( )、"aa"、1、True - key如果使用會變動的,會發生錯誤,例如: [ ] ![image](https://hackmd.io/_uploads/HJFAHeHtR.png) - 就算key是使用Tuple,但是Tuple裡面還包含有list[ ] ![image](https://hackmd.io/_uploads/BJNNUxrY0.png) ## 如何快速進入Python的REPL環境 ![image](https://hackmd.io/_uploads/BJubDxBFR.png) ## 會搞你的面試題目 ```py a = [[1, 2, 3]] double_a = a + a # [[1, 2, 3]] + [[1, 2, 3]] print(double_a) ``` ![image](https://hackmd.io/_uploads/Hy5mOeHt0.png) [情境說明] &emsp;因為兩個變數a是指向同一個記憶空間,是同樣的東西,所以只要變更其中一個,另外一個就會跟著變動,除非一開始就是不同得變數宣告 ```py a = [[1, 2, 3]] b = [[1, 2, 3]] a_b = a + b print(a_b) ``` ![image](https://hackmd.io/_uploads/SJ6PFlBtA.png) ## Expression vs Statement (表達式 vs 陳述句) - Expression (表達式) - `喜歡` `吃` `火鍋` 各自可以表達意思,但是不是完整句子 - 會有一個結果 ``` 18 "hello kitty" 123 > 999 5 + 1 hi () # 是表達式,因為都會有回傳值 ``` - Statement (陳述句) - 我喜歡吃火鍋 - 一個陳述句會包含多個表達式 - if、for、while ```py # count > 5 => 表達式,會有結果 # if count > 5: => 陳述句,不會有結果,因為if本身不會有結果 if count > 5: pass ``` ![image](https://hackmd.io/_uploads/BJ7H0gBtR.png) ## Lambda Expression (匿名函式) - lambda 參數列表: 表達式 ![image](https://hackmd.io/_uploads/BkZFgWSt0.png) 其中的 a + b 不能使用 return a + b 原因是因為return本身是Statement,return是回傳函數的結果,a + b是表達式 ```py lambda n: n + = 1 (x) 表達式不能賦值 lambda n: int : n + = 1 (x) 表達式不能宣告 ``` ## sort() vs sorted() - sord() - 會改變原本依附的串列內容(chars),chars.sort() ```py def has_x(s): if "x" in s: return 0 # "ddxddd"、"bxbb" 會跑這裡,因為有包含"x" return len(s) # "aa", "c" 會跑這裡,因為不包含"x",所以會計算長度 chars = ["aa", "ddxddd", "bxbb", "c"] chars.sort(key=lambda s: len(s)) # 沒有名字的表達式 匿名表達式 chars.sort(key=has_x) # 設定排序條件是以長度來排序 print(chars) ``` ![image](https://hackmd.io/_uploads/ryGIz-rKC.png) - sorted() - 不會改變原本串列內容 (推薦使用這方式) ※ ==串列排序最好是複製另外一份 不要修改到原本的串列== [情境一] ```py def has_x(s): if "x" in s: return 0 return len(s) chars = ["aa", "ddxddd", "bxbb", "c"] chars_sorted = sorted(chars, key=has_x) print(chars) # ['aa', 'ddxddd', 'bxbb', 'c'] print(chars_sorted) # ['ddxddd', 'bxbb', 'c', 'aa'] ``` [情境二] ```py def find_min_number(numbers): numbers.sort() # 會改變原本nums的順序,會影響到後續 return numbers nums = [9, 3, 0, 2] print(find_min_number(nums)) # [0, 2, 3, 9] print(nums) # [0, 2, 3, 9] ``` ## Closure (閉包) - 自由變數: 閉包內宣告的變數就是自由變數 ```py def hi(): message = "1111111"  #自由變數 def hey(): print(message) return hey # 回傳hey function,這裡還沒有執行 greeting = hi() # 回傳hey function,賦值給greeting變數 greeting() # 1111111 要注意到這裡才 "執行hey function" ``` ![image](https://hackmd.io/_uploads/ByiLcGBtA.png) ## Function Decorator (函數裝飾器) (面試題) [情境一] ```py def deprecated(fn): # fn => hi def wrapper(): print(f"Warning: {fn.__name__} is deprecated!") # 回傳function hi()的執行結果 print("Hello Kitty") return fn() # 因為有加小括號()所以會執行hi() # 回傳function wrapper 但沒執行 # wrapper內包含print(f"Warning: {fn.__name__} is deprecated!")和 # hi()執行的結果print("Hello Kitty") # 因為是閉包所以會被帶著走 return wrapper def hi(): print("Hello Kitty") # 高階函數 這裡回傳 還沒執行的wrapper function hi = deprecated(hi) # 回傳wrapper function賦值給hi變數 hi() # 這裡才執行wrapper() ``` ![image](https://hackmd.io/_uploads/HJ_C3MSKA.png) [情境二] ```py def deprecated(fn): # fn => hi # 這裡還沒有回傳執行 就印出過期 不合理要等到我執行wrapper才印出才合理 print("deprecated!") return fn() def wrapper(): print(f"Warning: {fn.__name__} is deprecated!") return fn() # 回傳function hi()的執行結果 print(...) return wrapper # 回傳function wrapper 但沒執行 def hi(): print("Hello Kitty") hi = deprecated(hi) print(hi) ``` ![image](https://hackmd.io/_uploads/ByroCGSYA.png) [情境三] ==使用 @deprecated== ```py def deprecated(fn): # fn => hi def wrapper(): print(f"Warning: {fn.__name__} is deprecated!") return fn() return wrapper # 函數裝飾器 function decorator # 這個hi函數會被打包丟進deprecated()內, # hi function會當作引數,相當於deprecated(fn)中的fn參數 @deprecated def hi(): print("Hello Kitty") hi() # 這裡才執行wrapper() ``` ![image](https://hackmd.io/_uploads/BJO1zXHF0.png) [情境四] ==要被打包的hi( ) function帶入引數== - `缺點`是每次要加引數,就要改參數個數,很麻煩,才會有`情境五` ```py def deprecated(fn): # fn => hi def wrapper(s): print(f"Warning: {fn.__name__} is deprecated!") return fn(s) return wrapper @deprecated def hi(s): print(f"Hello {s}") # hi = deprecated(hi) hi("cc") # hi === wrapper 已經不是原本的 hi ``` ![image](https://hackmd.io/_uploads/HJ6_4XrYA.png) [情境五] ==使用 *args **kwargs 帶入更多引數== ```py # def hi(*args, **kwargs): # # args = (1, 2) # # kwargs = {'name': 'cc', 'age': 18} # # print(*args, **kwargs) # print(*args, name="cc", age=18) def deprecated(fn): # 位置引數使用 *args # 關鍵字引數 **keyword args def wrapper(*args, **kwargs): print("Warning: deprecated!") # 這裡的 *args, **kwargs還沒解開 只是過水 return fn(*args, **kwargs) return wrapper # 函數裝飾器 function decorator @deprecated def hi(age, someone="kitty"): # 位置引數, 關鍵字引數 這裡才解開 print(f"Hello {age} {someone}") # age=18, someone="cc" @deprecated def hey(): pass hi(18, "cc") hey() ``` ![image](https://hackmd.io/_uploads/S1Pxq7rFA.png) [情境六] ==最後大魔王: 回傳裝飾器的裝飾器== ```py def deprecated(reason=""): def decorator(fn): def wrapper(*args, **kwargs): print("Warning: deprecated!") return fn(*args, **kwargs) return wrapper return decorator # 函數裝飾器 function decorator # 裝飾器上加參數 回傳裝飾器的裝飾器 # 會回傳裝飾器的裝飾器 # 這裡會回傳decorator() 表示會執行 def deprecated(reason="") 等於是回傳 @deprecated(reason="不想用了") def decorator(hi): def hi(age, someone="kitty"): print(f"Hello {age} {someone}") @deprecated def hey(): pass # hi(18, "cc") # hey() ``` ## Generaotor (產生器) - 使用小括號( )內寫推導式,但==不是Tuple== - ==Tuple沒有推導式== - 只要有==印出來==就算是拿出來了 ![image](https://hackmd.io/_uploads/Byv6hmHF0.png) [情境一]  ==yeild== 只要function內有寫到 yeild 就是產生器 ```py def even_numbers(n): i = 1 while i <= n: if i % 2 == 0: yield i # 跑到這裡會先停住,一開始會是2,除非有人來拿,d才會再丟出去 i += 1 nn = even_numbers(10) ``` ![image](https://hackmd.io/_uploads/H1qppQHYC.png) ## Recursive (遞迴) (費波那契數) - 面試問的話,應該就是在問遞迴 - 自己呼叫自己 ```py # fib def fib(n): if n == 0: return 0 if n == 1: return 1 return fib(n-1) + fib(n - 2) print(fib(5)) ``` ![image](https://hackmd.io/_uploads/r1Mj0XBKA.png) ## Module 模組 ```py # hi.py from xyz.ccc import add, ccc, xyz, mm print(__name__) # __main__ print(xyz) print(mm.pi) ``` ```py # ccc.py import math as mm def add(a, b): return a + b def ccc(): pass print(456) xyz = "9999" # 如果是單獨執行 __main__ # 被import 會印出 __name__ => xyz.cc print(__name__) # 是內建變數 __name__ if __name__ == "__main__": if add(1, 2) == 3: print("ok") else: print("ng") ``` ![image](https://hackmd.io/_uploads/B1fTJ4SFR.png) ![image](https://hackmd.io/_uploads/HJ3R1ESK0.png)