Generators, Decorators & Closures
函式內定義另一個函式,內部函式可以記住外部函式的變數:
def make_counter(start=0):
count = start # 外部變數
def counter(): # 內部函式
nonlocal count
count += 1
return count
return counter # 回傳函式本身
c1 = make_counter()
c2 = make_counter(10)
print(c1()) # 1
print(c1()) # 2
print(c2()) # 11(各自獨立)
在不修改函式本身的情況下,為函式增加額外功能:
def log(func): # 裝飾器函式
def wrapper(*args, **kwargs):
print(f"呼叫 {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} 完成")
return result
return wrapper
@log # 等同於 add = log(add)
def add(a, b):
return a + b
print(add(3, 5))
# 呼叫 add
# add 完成
# 8
import time
# 計算執行時間
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 執行時間:{end-start:.4f} 秒")
return result
return wrapper
@timer
def slow_sum(n):
return sum(range(n))
slow_sum(1_000_000)
# slow_sum 執行時間:0.0312 秒
用 yield 替代 return,一次產生一個值,節省記憶體:
# 一般函式 — 一次回傳整個清單
def normal_range(n):
return list(range(n)) # 全部存在記憶體
# 生成器 — 一次產生一個
def my_range(n):
i = 0
while i < n:
yield i # 暫停在這裡,下次呼叫繼續
i += 1
for num in my_range(5):
print(num) # 0 1 2 3 4
# 生成器表達式
squares = (x**2 for x in range(10)) # 用 () 而非 []
with 語法)# 自訂 context manager
class Timer:
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, *args):
elapsed = time.time() - self.start
print(f"耗時 {elapsed:.4f} 秒")
with Timer() as t:
result = sum(range(1_000_000))
print(result)
# 執行完後自動印出耗時
# 也可以用 @contextmanager 裝飾器
from contextlib import contextmanager
@contextmanager
def managed_resource():
print("取得資源")
yield
print("釋放資源")
Python 3.5+ 支援型別提示,讓程式碼更易讀、IDE 更好提示:
def greet(name: str, times: int = 1) -> str:
return (f"Hello, {name}! " * times).strip()
def process_scores(scores: list[int]) -> dict[str, float]:
return {
"max": max(scores),
"min": min(scores),
"avg": sum(scores) / len(scores)
}
from typing import Optional
def find_user(user_id: int) -> Optional[dict]:
# 可能回傳 dict 或 None
...
# 組合使用:帶快取的 Fibonacci 生成器
def memoize(func):
cache = {}
def wrapper(n):
if n not in cache:
cache[n] = func(n)
return cache[n]
return wrapper
@memoize
def fib(n: int) -> int:
if n <= 1:
return n
return fib(n-1) + fib(n-2)
# 生成前 20 個 Fibonacci 數
fib_gen = (fib(i) for i in range(20))
print(list(fib_gen))
學會了閉包、裝飾器、生成器與型別提示。