← 返回大綱
第十一章

進階功能

Generators, Decorators & Closures

閉包

閉包 (Closure)

函式內定義另一個函式,內部函式可以記住外部函式的變數:

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(各自獨立)
裝飾器

裝飾器 (Decorator)

在不修改函式本身的情況下,為函式增加額外功能:

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 秒
生成器

生成器 (Generator)

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))  # 用 () 而非 []
Context Manager

Context Manager (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("釋放資源")
型別提示

型別提示 (Type Hints)

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

第十一章完成!

學會了閉包、裝飾器、生成器與型別提示。