Python3:装饰器、生成器与迭代器
一、🎭 装饰器:给函数穿上"魔法外衣"
想象一下,如果函数是演员,装饰器就是能瞬间更换的戏服,让演员不改变本身,却能展现出全新的能力和特性。
装饰器基本概念
装饰器是一个接收函数作为参数并返回一个新函数的高阶函数,它能在不修改原函数代码的情况下,增强原函数的功能。
# 最简单的装饰器
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 result
return wrapper
@smart_decorator
def add(a, b):
return a + b
print(add(3, 5)) # 使用装饰器包装的函数
# 输出:
# 🔍 调用函数: add
# ✅ 函数返回: 8
# 8
带参数的装饰器
如果我们想让装饰器本身也能接收参数,需要再包装一层函数:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"你好, {name}!")
return "打招呼完成"
greet("小明")
# 输出:
# 你好, 小明!
# 你好, 小明!
# 你好, 小明!
functools.wraps:保留原函数的元信息
默认情况下,装饰器会替换原函数,导致原函数的名称、文档字符串等元信息丢失。使用functools.wraps
可以解决这个问题:
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
"""这是包装函数的文档"""
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def example():
"""这是原函数的文档"""
print("Inside the function")
print(example.__name__) # 输出: example (而不是 wrapper)
print(example.__doc__) # 输出: 这是原函数的文档
实用装饰器示例
1. 计时器装饰器
import time
import functools
def 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 result
return 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] = result
print(f"📝 缓存新结果: {args} -> {result}")
return result
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return 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 wrapper
return 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 wrapper
class Calculator:
def __init__(self, name):
self.name = name
@log_method_calls
def add(self, a, b):
return a + b
@log_method_calls
def multiply(self, a, b):
return a * b
calc = 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_hello
return cls
@add_greeting
class Person:
def __init__(self, name):
self.name = name
p = Person("张三")
print(p.say_hello()) # 输出: 张三 说:你好!
二、🔄 迭代器:数据流的"传送带"
迭代器基本概念
迭代器是一种特殊的对象,它实现了迭代协议,允许我们逐个访问集合中的元素,而不需要知道集合的底层结构。
在Python中,迭代器需要实现两个方法:
__iter__()
: 返回迭代器对象本身__next__()
: 返回下一个元素,如果没有更多元素则抛出StopIteration
异常
# 简单迭代器示例
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
# 返回迭代器对象自身
return self
def __next__(self):
# 如果计数结束,抛出StopIteration
if self.current <= 0:
raise StopIteration
# 否则返回当前值并递减
value = self.current
self.current -= 1
return 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':
break
yield 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 i
except 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 * i
def 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 False
if n <= 3:
return True
if n % 2 == 0 or n % 3 == 0:
return False
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
def infinite_primes():
"""无限生成素数"""
num = 2
while True:
if is_prime(num):
yield num
num += 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 item
def filter_even(items):
for item in items:
if item % 2 == 0:
yield item
def multiply_by_2(items):
for item in items:
yield item * 2
def 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 value
return 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 = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
result = self.process(self.data[self.index])
self.index += 1
return result
def 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 result
return 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.8
yield 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的函数式编程特性