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


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

## Tuple vs List (元組 vs 串列)
- Tuple 異質性 => (1, "a", True)
- Tuple表示方式
```py
a = (1, 2, 3)
```

```py
a = 1, 2, 3
```

```py
a = 1, # 單一個一定要在最後面加上逗號,才能組合成一Tuple
```

- List 同質性 => [1, 2, 3, 4]
## Set (集合)
- 可以放`多種資料型態`
- 排列`沒有順序`
- 序列中的元素`不會重複`
- 沒有辦法透過索引值取得資料,因為沒有索引值
- 可以透過list轉成串列後,再取資料
- 空集合寫法
```py
aa = set()
```

## dict (字典)
- 字典表示方式
```py
cat = {"name": "kitty", "age": 18}
print(cat)
print(type(cat))
```


- 雜湊表
- 可以透過座標位置從表格中找到key和value
- 使用字典,其實底下會去找雜湊表。那為什麼會取名字典,是因為Python本身就有內建雜湊函數hash()

- hash( )的 key 設定注意事項
- key一定要是不可變動的,例如:Tuple( )、"aa"、1、True
- key如果使用會變動的,會發生錯誤,例如: [ ]

- 就算key是使用Tuple,但是Tuple裡面還包含有list[ ]

## 如何快速進入Python的REPL環境

## 會搞你的面試題目
```py
a = [[1, 2, 3]]
double_a = a + a # [[1, 2, 3]] + [[1, 2, 3]]
print(double_a)
```

[情境說明]
 因為兩個變數a是指向同一個記憶空間,是同樣的東西,所以只要變更其中一個,另外一個就會跟著變動,除非一開始就是不同得變數宣告
```py
a = [[1, 2, 3]]
b = [[1, 2, 3]]
a_b = a + b
print(a_b)
```

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

## Lambda Expression (匿名函式)
- lambda 參數列表: 表達式

其中的 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)
```

- 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"
```

## 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()
```

[情境二]
```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)
```

[情境三] ==使用 @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()
```

[情境四] ==要被打包的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
```

[情境五] ==使用 *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()
```

[情境六] ==最後大魔王: 回傳裝飾器的裝飾器==
```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沒有推導式==
- 只要有==印出來==就算是拿出來了

[情境一] ==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)
```

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

## 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")
```

