---
# System prepended metadata

title: 20240729 Python
tags: [Python]

---

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