Python3:裝飾器、生成器與迭代器
- 一、🎭 裝飾器:給函數穿上"魔法外衣"
- 裝飾器基本概念
- 為裝飾器添加參數傳遞功能
- 帶參數的裝飾器
- functools.wraps:保留原函數的元信息
- 實用裝飾器示例
- 1. 計時器裝飾器
- 2. 緩存裝飾器(Memoization)
- 3. 權限檢查裝飾器
- 4. 類方法裝飾器
- 5. 類裝飾器
- 二、🔄 迭代器:數據流的"傳送帶"
- 迭代器基本概念
- 內置迭代器工具
- 🌱 生成器:懶惰的迭代器
- 生成器函數
- 生成器表達式
- 生成器的內存效率
- 生成器的進階特性
- 1. 雙向通信:send() 方法
- 2. 生成器的異常處理:throw() 和 close()
- 3. 委托生成器:yield from
- 實用生成器示例
- 1. 文件讀取生成器
- 2. 無限素數生成器
- 3. 數據流處理管道
- 三、🔗 將裝飾器、迭代器和生成器結合使用
- 裝飾生成器函數
- 可重用的迭代器類
- 四、 📊 實際應用案例:數據分析管道
- 總結
- 1. 裝飾器
- 2. 迭代器
- 3. 生成器
- 練習建議
一、🎭 裝飾器:給函數穿上"魔法外衣"
想象一下,如果函數是演員,裝飾器就是能瞬間更換的戲服,讓演員不改變本身,卻能展現出全新的能力和特性。
裝飾器基本概念
裝飾器是一個接收函數作為參數并返回一個新函數的高階函數,它能在不修改原函數代碼的情況下,增強原函數的功能。
# 最簡單的裝飾器
def simple_decorator(func):def wrapper():print("🌟 函數執行前")func()print("🌟 函數執行后")return wrapper# 使用裝飾器
@simple_decorator
def say_hello():print("Hello, World!")# 調用被裝飾的函數
say_hello()
# 輸出:
# 🌟 函數執行前
# Hello, World!
# 🌟 函數執行后
這里的@simple_decorator
語法糖等同于:
say_hello = simple_decorator(say_hello)
為裝飾器添加參數傳遞功能
實際使用中,原函數通常有參數,我們需要確保裝飾器能正確傳遞這些參數:
def smart_decorator(func):def wrapper(*args, **kwargs):print(f"🔍 調用函數: {func.__name__}")result = func(*args, **kwargs)print(f"? 函數返回: {result}")return resultreturn wrapper@smart_decorator
def add(a, b):return a + bprint(add(3, 5)) # 使用裝飾器包裝的函數
# 輸出:
# 🔍 調用函數: add
# ? 函數返回: 8
# 8
帶參數的裝飾器
如果我們想讓裝飾器本身也能接收參數,需要再包裝一層函數:
def repeat(times):def decorator(func):def wrapper(*args, **kwargs):result = Nonefor _ in range(times):result = func(*args, **kwargs)return resultreturn wrapperreturn decorator@repeat(times=3)
def greet(name):print(f"你好, {name}!")return "打招呼完成"greet("小明")
# 輸出:
# 你好, 小明!
# 你好, 小明!
# 你好, 小明!
functools.wraps:保留原函數的元信息
默認情況下,裝飾器會替換原函數,導致原函數的名稱、文檔字符串等元信息丟失。使用functools.wraps
可以解決這個問題:
import functoolsdef my_decorator(func):@functools.wraps(func) # 保留原函數的元信息def wrapper(*args, **kwargs):"""這是包裝函數的文檔"""print("Before function call")result = func(*args, **kwargs)print("After function call")return resultreturn wrapper@my_decorator
def example():"""這是原函數的文檔"""print("Inside the function")print(example.__name__) # 輸出: example (而不是 wrapper)
print(example.__doc__) # 輸出: 這是原函數的文檔
實用裝飾器示例
1. 計時器裝飾器
import time
import functoolsdef timer(func):@functools.wraps(func)def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"?? 函數 {func.__name__} 執行耗時: {end_time - start_time:.4f} 秒")return resultreturn wrapper@timer
def slow_function():time.sleep(1)return "函數執行完成"slow_function()
# 輸出: ?? 函數 slow_function 執行耗時: 1.0012 秒
2. 緩存裝飾器(Memoization)
def memoize(func):cache = {}@functools.wraps(func)def wrapper(*args):if args in cache:print(f"💾 使用緩存結果: {args}")return cache[args]result = func(*args)cache[args] = resultprint(f"📝 緩存新結果: {args} -> {result}")return resultreturn wrapper@memoize
def fibonacci(n):if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10))
# 首次計算會緩存所有中間結果
print(fibonacci(10)) # 第二次直接使用緩存
3. 權限檢查裝飾器
def require_auth(role):def decorator(func):@functools.wraps(func)def wrapper(user, *args, **kwargs):if user.get('role') != role:raise PermissionError(f"需要 {role} 權限!")return func(user, *args, **kwargs)return wrapperreturn decorator@require_auth(role="admin")
def delete_user(current_user, user_id):print(f"用戶 {user_id} 已被刪除")# 測試權限
admin_user = {'name': 'Admin', 'role': 'admin'}
normal_user = {'name': 'User', 'role': 'user'}delete_user(admin_user, 123) # 正常執行
try:delete_user(normal_user, 123) # 拋出權限錯誤
except PermissionError as e:print(f"錯誤: {e}")
4. 類方法裝飾器
裝飾器也可以用于類方法:
def log_method_calls(func):@functools.wraps(func)def wrapper(self, *args, **kwargs):print(f"📞 調用方法 {self.__class__.__name__}.{func.__name__}")return func(self, *args, **kwargs)return wrapperclass Calculator:def __init__(self, name):self.name = name@log_method_callsdef add(self, a, b):return a + b@log_method_callsdef multiply(self, a, b):return a * bcalc = Calculator("科學計算器")
calc.add(5, 3) # 輸出: 📞 調用方法 Calculator.add
calc.multiply(5, 3) # 輸出: 📞 調用方法 Calculator.multiply
5. 類裝飾器
裝飾器不僅可以裝飾函數,還可以裝飾整個類:
def add_greeting(cls):# 給類添加一個新方法def say_hello(self):return f"{self.name} 說:你好!"cls.say_hello = say_helloreturn cls@add_greeting
class Person:def __init__(self, name):self.name = namep = Person("張三")
print(p.say_hello()) # 輸出: 張三 說:你好!
二、🔄 迭代器:數據流的"傳送帶"
迭代器基本概念
迭代器是一種特殊的對象,它實現了迭代協議,允許我們逐個訪問集合中的元素,而不需要知道集合的底層結構。
在Python中,迭代器需要實現兩個方法:
__iter__()
: 返回迭代器對象本身__next__()
: 返回下一個元素,如果沒有更多元素則拋出StopIteration
異常
# 簡單迭代器示例
class CountDown:def __init__(self, start):self.current = startdef __iter__(self):# 返回迭代器對象自身return selfdef __next__(self):# 如果計數結束,拋出StopIterationif self.current <= 0:raise StopIteration# 否則返回當前值并遞減value = self.currentself.current -= 1return value# 使用for循環遍歷迭代器
for num in CountDown(5):print(num)
# 輸出: 5 4 3 2 1# 手動使用迭代器
iterator = iter(CountDown(3)) # 調用__iter__()
print(next(iterator)) # 3 # 調用__next__()
print(next(iterator)) # 2
print(next(iterator)) # 1
# print(next(iterator)) # 拋出StopIteration異常
內置迭代器工具
Python的itertools
模塊提供了許多強大的迭代器工具:
import itertools# 無限迭代器
counter = itertools.count(1) # 從1開始計數
print([next(counter) for _ in range(5)]) # [1, 2, 3, 4, 5]# 循環迭代器
cycle = itertools.cycle(["紅", "黃", "綠"])
print([next(cycle) for _ in range(5)]) # ['紅', '黃', '綠', '紅', '黃']# 重復迭代器
repeat = itertools.repeat("A", 3)
print(list(repeat)) # ['A', 'A', 'A']# 鏈接多個迭代器
chain = itertools.chain([1, 2], [3, 4], [5, 6])
print(list(chain)) # [1, 2, 3, 4, 5, 6]# 分組迭代器
data = ["蘋果", "梨", "菠蘿", "葡萄", "芒果"]
for k, group in itertools.groupby(sorted(data), key=len):print(f"{k}個字符: {list(group)}")
# 輸出:
# 1個字符: ['梨']
# 2個字符: ['蘋果', '菠蘿', '芒果', '葡萄']# 排列組合
print(list(itertools.combinations("ABC", 2))) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
print(list(itertools.permutations("ABC", 2))) # [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
🌱 生成器:懶惰的迭代器
生成器是一種特殊的迭代器,它使用函數的方式創建,通過yield
關鍵字返回值,并保存函數的執行狀態,等待下次調用。
生成器函數
使用yield
關鍵字的函數會變成生成器函數,調用時返回一個生成器對象:
def countdown(n):print("開始倒計時!")while n > 0:yield n # 返回當前值并暫停函數執行n -= 1# 創建生成器對象
generator = countdown(3)
print(type(generator)) # <class 'generator'># 迭代生成器
print(next(generator)) # 開始倒計時! 3
print(next(generator)) # 2
print(next(generator)) # 1
# print(next(generator)) # StopIteration異常# 使用for循環更方便
for num in countdown(3):print(num)
# 輸出:
# 開始倒計時!
# 3
# 2
# 1
生成器表達式
類似于列表推導式,但使用圓括號而不是方括號:
# 列表推導式:立即計算所有結果并存儲在內存中
squares_list = [x**2 for x in range(5)]
print(squares_list) # [0, 1, 4, 9, 16]# 生成器表達式:按需計算
squares_gen = (x**2 for x in range(5))
print(squares_gen) # <generator object <genexpr> at 0x...># 使用生成器
for square in squares_gen:print(square) # 0 1 4 9 16
生成器的內存效率
生成器的主要優勢是內存效率,特別是處理大數據集時:
import sys# 比較列表和生成器的內存使用
def get_size(obj):return sys.getsizeof(obj)# 創建一個大范圍
big_range = 10**6# 使用列表
big_list = [x for x in range(big_range)]
print(f"列表大小: {get_size(big_list)} 字節")# 使用生成器
big_gen = (x for x in range(big_range))
print(f"生成器大小: {get_size(big_gen)} 字節")# 雖然兩者都可以提供相同的數字序列,但內存占用差異巨大
生成器的進階特性
1. 雙向通信:send() 方法
生成器不僅可以產生值,還可以接收外部發送的值:
def echo_generator():while True:received = yield "等待輸入..." # yield一個值,然后等待send()的輸入if received == 'exit':breakyield f"你說: {received}" # 返回處理后的輸入# 創建生成器
echo = echo_generator()
print(next(echo)) # 啟動生成器: "等待輸入..."print(echo.send("你好")) # 發送值并獲取下一個yield的值: "你說: 你好"
print(next(echo)) # "等待輸入..."
print(echo.send("Python")) # "你說: Python"
print(next(echo)) # "等待輸入..."
echo.send("exit") # 結束生成器
2. 生成器的異常處理:throw() 和 close()
生成器可以接收和處理外部拋入的異常:
def number_generator():try:for i in range(5):yield iexcept ValueError:print("捕獲到ValueError!")yield "錯誤處理完畢"finally:print("生成器清理資源...")gen = number_generator()
print(next(gen)) # 0
print(next(gen)) # 1
print(gen.throw(ValueError)) # 拋出異常,輸出: 捕獲到ValueError! 錯誤處理完畢
gen.close() # 關閉生成器,觸發finally: 生成器清理資源...
3. 委托生成器:yield from
yield from
允許一個生成器委托給另一個生成器:
def subgenerator(n):for i in range(n):yield i * idef delegating_generator():# 等同于迭代subgenerator并yield每個值yield from subgenerator(5)yield "子生成器完成"for value in delegating_generator():print(value)
# 輸出: 0 1 4 9 16 子生成器完成
實用生成器示例
1. 文件讀取生成器
逐行讀取大文件而不將整個文件載入內存:
def read_large_file(file_path):with open(file_path, 'r') as file:for line in file:yield line.strip()# 使用示例
# for line in read_large_file("very_large_file.txt"):
# process(line)
2. 無限素數生成器
def is_prime(n):"""檢查一個數是否為素數"""if n <= 1:return Falseif n <= 3:return Trueif n % 2 == 0 or n % 3 == 0:return Falsei = 5while i * i <= n:if n % i == 0 or n % (i + 2) == 0:return Falsei += 6return Truedef infinite_primes():"""無限生成素數"""num = 2while True:if is_prime(num):yield numnum += 1# 獲取前10個素數
prime_gen = infinite_primes()
first_10_primes = [next(prime_gen) for _ in range(10)]
print(first_10_primes) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
3. 數據流處理管道
使用生成器構建數據處理管道:
def read_data():data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]for item in data:yield itemdef filter_even(items):for item in items:if item % 2 == 0:yield itemdef multiply_by_2(items):for item in items:yield item * 2def pipeline():# 構建處理管道result = multiply_by_2(filter_even(read_data()))return list(result)print(pipeline()) # [4, 8, 12, 16, 20]
三、🔗 將裝飾器、迭代器和生成器結合使用
這些高級特性可以很好地結合使用,創建更強大的功能:
裝飾生成器函數
def debug_generator(func):@functools.wraps(func)def wrapper(*args, **kwargs):gen = func(*args, **kwargs)for value in gen:print(f"🔍 生成器 {func.__name__} 產生: {value}")yield valuereturn wrapper@debug_generator
def counting(n):for i in range(n):yield i# 使用被裝飾的生成器
for num in counting(3):print(f"使用值: {num}")
# 輸出:
# 🔍 生成器 counting 產生: 0
# 使用值: 0
# 🔍 生成器 counting 產生: 1
# 使用值: 1
# 🔍 生成器 counting 產生: 2
# 使用值: 2
可重用的迭代器類
創建一個更復雜的可重用迭代器類:
class DataProcessor:def __init__(self, data):self.data = dataself.index = 0def __iter__(self):return selfdef __next__(self):if self.index >= len(self.data):raise StopIterationresult = self.process(self.data[self.index])self.index += 1return resultdef process(self, item):# 可在子類中重寫的處理方法return item# 通過繼承擴展功能
class SquareProcessor(DataProcessor):def process(self, item):return item ** 2# 使用迭代器
numbers = [1, 2, 3, 4, 5]
processor = SquareProcessor(numbers)
print(list(processor)) # [1, 4, 9, 16, 25]
四、 📊 實際應用案例:數據分析管道
結合所有概念創建一個數據處理管道:
import time
import functools# 裝飾器:用于性能監控
def monitor(func):@functools.wraps(func)def wrapper(*args, **kwargs):start = time.time()result = func(*args, **kwargs)end = time.time()print(f"?? {func.__name__} 處理耗時: {end - start:.4f}秒")return resultreturn wrapper# 生成器:數據讀取
@monitor
def read_data(filename):"""模擬從文件讀取數據"""print(f"📂 從{filename}讀取數據...")# 模擬數據data = [{"id": 1, "name": "Product A", "price": 100, "category": "Electronics"},{"id": 2, "name": "Product B", "price": 50, "category": "Clothing"},{"id": 3, "name": "Product C", "price": 150, "category": "Electronics"},{"id": 4, "name": "Product D", "price": 80, "category": "Home"},{"id": 5, "name": "Product E", "price": 200, "category": "Electronics"}]time.sleep(0.1) # 模擬I/O延遲for item in data:yield item# 生成器:數據過濾
@monitor
def filter_data(items, category):"""篩選特定類別的商品"""print(f"🔍 篩選{category}類別...")for item in items:if item["category"] == category:yield item# 生成器:數據轉換
@monitor
def transform_data(items):"""計算商品價格的銷售價(打八折)"""print("🔄 計算銷售價...")for item in items:transformed = item.copy()transformed["sale_price"] = item["price"] * 0.8yield transformed# 消費生成器:保存結果
@monitor
def save_results(items, output_filename):"""保存處理后的數據"""print(f"💾 保存結果到{output_filename}...")results = list(items) # 消耗生成器# 模擬保存操作time.sleep(0.1)print(f"? 已保存{len(results)}條記錄")return results# 構建完整的數據處理管道
def process_sales_data(input_filename, output_filename, category):"""完整的數據處理流程"""# 構建處理管道data = read_data(input_filename)filtered_data = filter_data(data, category)transformed_data = transform_data(filtered_data)results = save_results(transformed_data, output_filename)# 打印結果示例if results:print("\n📊 處理結果示例:")for item in results[:2]: # 只顯示前兩條print(f" {item['name']}: 原價¥{item['price']}, 銷售價¥{item['sale_price']:.2f}")if len(results) > 2:print(f" ...以及其他{len(results)-2}條記錄")# 執行數據處理管道
process_sales_data("products.csv", "electronics_sales.csv", "Electronics")
總結
1. 裝飾器
- 允許在不修改原函數的情況下添加新功能
- 可以用于函數或類
- 實用場景:日志記錄、性能監控、訪問控制、緩存等
2. 迭代器
- 實現了
__iter__
和__next__
方法的對象 - 允許逐個訪問集合元素,而不需要加載整個集合
- Python內置了豐富的迭代器工具(
itertools
)
3. 生成器
- 使用
yield
語句的特殊函數 - 執行時會保存狀態,按需生成值
- 極大減少內存使用,適合處理大數據集
- 高級特性:
send()
,throw()
,close()
,yield from
這三者是Python高級編程中的關鍵概念,掌握它們可以編寫出更優雅、高效和可維護的代碼。尤其在處理大量數據、構建數據處理管道或需要分布式計算時,這些概念尤為重要。
練習建議
-
構建裝飾器庫
- 創建一組實用裝飾器:計時、重試、緩存等
- 嘗試組合多個裝飾器
-
設計自定義迭代器
- 實現一個模擬數據庫游標的迭代器
- 創建一個分頁迭代器,按批次返回數據
-
生成器應用
- 實現一個大文件處理程序,使用生成器進行內存高效處理
- 構建數據轉換管道,將數據從一種格式轉換為另一種格式
-
綜合項目
- 開發一個簡單的ETL(提取-轉換-加載)工具
- 設計一個支持鏈式操作的數據處理框架
🚀 下一步學習:探索Python的函數式編程特性