10.Python 迭代器、生成器与装饰器 深度解析
Python 迭代器、生成器与装饰器 深度解析
目录
- 迭代器
- 1.1 可迭代对象与迭代器协议
- 1.2 手动实现迭代器
- 1.3
itertools模块掠影
- 生成器
- 2.1 生成器函数与
yield - 2.2 生成器表达式
- 2.3
yield from委托 - 2.4 生成器的高级特性:
send、throw、close
- 2.1 生成器函数与
- 装饰器
- 3.1 高阶函数与闭包回顾
- 3.2 装饰器原理与
@语法 - 3.3 保留元数据:
functools.wraps - 3.4 带参数的装饰器
- 3.5 类装饰器
- 3.6 多个装饰器的叠加顺序
- 3.7 实战示例:日志、计时、权限检查
- 总结与最佳实践
1. 迭代器
1.1 可迭代对象与迭代器协议
在 Python 中,可以用for循环遍历的对象都称为可迭代对象(Iterable),如列表、元组、字典、集合、字符串、文件等。可迭代对象的内部实现了__iter__方法,该方法返回一个迭代器(Iterator)。
迭代器是实现了__next__方法的对象,每次调用返回下一个元素,当元素耗尽时抛出StopIteration异常。迭代器本身也必须实现__iter__方法(通常返回自身),所以迭代器也是可迭代对象。
# 查看可迭代对象与迭代器lst=[1,2,3]it=iter(lst)# 调用 __iter__,返回列表迭代器print(type(it))# <class 'list_iterator'>print(next(it))# 1print(next(it))# 2print(next(it))# 3# next(it) → StopIterationfor循环等价于以下过程:
it=iter(iterable)whileTrue:try:item=next(it)exceptStopIteration:break# 处理 item1.2 手动实现迭代器
我们可以自定义可迭代的类,通过实现__iter__和__next__来控制迭代逻辑。
classCountdown:"""从 start 倒数到 1 的迭代器"""def__init__(self,start):self.current=startdef__iter__(self):returnselfdef__next__(self):ifself.current<1:raiseStopIteration num=self.current self.current-=1returnnumforninCountdown(3):print(n,end=' ')# 3 2 1若要创建不可重复迭代的迭代器,可以像上面那样让__iter__返回self。若希望每次iter()调用都能生成新的迭代器(可重复遍历),则需要分离“可迭代对象”和“迭代器”:
classFibonacci:"""生成指定个数的斐波那契数"""def__init__(self,max_count):self.max=max_countdef__iter__(self):# 返回新的迭代器实例returnFibIterator(self.max)classFibIterator:def__init__(self,max_count):self.max=max_count self.a,self.b=0,1self.count=0def__iter__(self):returnselfdef__next__(self):ifself.count>=self.max:raiseStopIteration val=self.a self.a,self.b=self.b,self.a+self.b self.count+=1returnvalforninFibonacci(5):print(n)# 0, 1, 1, 2, 31.3itertools模块掠影
itertools是内置的高效迭代器工具集,值得一览:
importitertools# 无限迭代器c=itertools.count(10,2)# 10, 12, 14, ...# cy = itertools.cycle('AB') # A B A B ...# rp = itertools.repeat(5, 3) # 5 5 5# 排列组合forpinitertools.permutations('AB',2):# ('A','B'), ('B','A')passforcinitertools.combinations('ABC',2):# ('A','B'), ('A','C'), ('B','C')# 链式与压缩joined=itertools.chain([1,2],[3,4])# 1 2 3 42. 生成器
2.1 生成器函数与yield
生成器是一种用函数语法实现的迭代器。在函数内部使用yield关键字,该函数就变成了生成器函数,调用它不会执行函数体,而是返回一个生成器对象。
每次对生成器调用next()时,函数会执行到下一个yield语句并暂停,返回值传回调用者。下一次再调用next()时,从上次暂停处继续执行,直到函数结束触发StopIteration。
defsimple_gen():print("开始")yield1print("继续")yield2print("结束")g=simple_gen()print(next(g))# 开始 \n 1print(next(g))# 继续 \n 2# next(g) → 结束 \n StopIteration生成器特适合处理大数据流,因为它是惰性求值的,需要时才产生下一个值,内存占用极小。
defread_large_file(file_path):withopen(file_path)asf:forlineinf:yieldline.strip()# 永远不会将整个文件加载到内存forlineinread_large_file("giant.txt"):process(line)2.2 生成器表达式
与列表推导式类似,但使用圆括号,产生一个生成器对象而不是一次性列表。适用于无需存储全部结果的情况。
squares=(x**2forxinrange(1000000))print(next(squares))# 0print(next(squares))# 1# 作为函数参数时可以省略一层括号total=sum(x**2forxinrange(1000000))2.3yield from委托
yield from语法允许将一个生成器的所有值委托给另一个生成器,极大简化嵌套生成器的编写。
defflatten(items):foriteminitems:ifisinstance(item,(list,tuple)):yieldfromflatten(item)else:yielditem nested=[1,[2,[3,4],5],6]print(list(flatten(nested)))# [1, 2, 3, 4, 5, 6]2.4 生成器的高级特性:send、throw、close
生成器不仅可以生产数据,还可以通过send()接收外部传入的值,实现协程式的双向通信。
defecho():whileTrue:received=yieldprint(f"收到:{received}")g=echo()next(g)# 预激活生成器(推进至第一个 yield)g.send("Hello")# 收到: Hellog.send("World")# 收到: Worldthrow(type, value, traceback):在生成器暂停处抛出一个异常。close():在生成器暂停处抛出GeneratorExit异常,用于终止生成器。
defcontrolled():try:whileTrue:yieldexceptGeneratorExit:print("生成器已关闭")g=controlled()next(g)g.close()# 生成器已关闭这些高级特性是早期asyncio的基础,尽管如今异步编程主要使用async/await,理解它们仍有助于深入 Python 的运行模型。
3. 装饰器
3.1 高阶函数与闭包回顾
装饰器是高阶函数的典型应用:接受一个函数作为参数,并返回一个新函数。闭包特性使得内部函数可以捕获外部函数的变量。
defouter(x):definner(y):returnx+yreturninner add_five=outer(5)print(add_five(3))# 83.2 装饰器原理与@语法
装饰器本质上是一个接受函数并返回增强版函数的可调用对象。@decorator语法只是把目标函数传入装饰器并赋值的简写。
defuppercase(func):defwrapper(*args,**kwargs):result=func(*args,**kwargs)ifisinstance(result,str):returnresult.upper()returnresultreturnwrapper@uppercasedefgreet(name):returnf"hello,{name}"print(greet("Alice"))# HELLO, ALICE# 等价于: greet = uppercase(greet)装饰器可以在函数之前或之后插入额外逻辑,也可以修改输入参数和返回值。
3.3 保留元数据:functools.wraps
直接返回wrapper会丢失原函数的名称、文档字符串、参数列表等元数据。使用functools.wraps可以将这些属性复制到wrapper上。
fromfunctoolsimportwrapsdeftimer(func):@wraps(func)defwrapper(*args,**kwargs):importtime start=time.time()result=func(*args,**kwargs)end=time.time()print(f"{func.__name__}执行时间:{end-start:.4f}s")returnresultreturnwrapper@timerdefslow_add(a,b):"""返回两数之和(缓慢版)"""importtime time.sleep(0.5)returna+bprint(slow_add.__name__)# slow_add (若不使用 wraps 则是 wrapper)print(slow_add.__doc__)# 返回两数之和(缓慢版)help(slow_add)3.4 带参数的装饰器
如果装饰器本身需要参数(比如重复次数),则需要再包装一层装饰器工厂:外层函数接受配置参数,返回真正的装饰器。
defrepeat(times):defdecorator(func):@wraps(func)defwrapper(*args,**kwargs):for_inrange(times):result=func(*args,**kwargs)returnresultreturnwrapperreturndecorator@repeat(3)defsay_hello():print("Hello!")say_hello()# 打印 3 次 Hello!@repeat(3)首先调用repeat(3)返回decorator,然后decorator(say_hello)返回wrapper。
3.5 类装饰器
类也可以作为装饰器,只要实现了__call__方法。类装饰器在需要维护状态时更自然。
classCountCalls:def__init__(self,func):self.func=func self.count=0def__call__(self,*args,**kwargs):self.count+=1print(f"{self.func.__name__}已被调用{self.count}次")returnself.func(*args,**kwargs)@CountCallsdefhello():print("Hello")hello()# hello 已被调用 1 次 \n Hellohello()# hello 已被调用 2 次 \n Hello带参数的类装饰器同样可以通过__init__接收配置参数,在__call__内根据参数创建并返回一个wrapper函数。
3.6 多个装饰器的叠加顺序
多个装饰器可以从下往上依次应用(等价于从里往外调用)。
@timer@uppercasedefgreet(name):returnf"hello,{name}"# 等价于: greet = timer(uppercase(greet))调用greet("Alice")的流程:
uppercase的wrapper首先执行原greet,将返回值转为大写。- 然后
timer的wrapper测量整体耗时。
装饰器顺序会影响行为,务必理清执行次序。
3.7 实战示例:日志、计时、权限检查
日志装饰器
deflog_function_call(func):@wraps(func)defwrapper(*args,**kwargs):print(f"[LOG] 调用{func.__name__}, 参数:{args},{kwargs}")result=func(*args,**kwargs)print(f"[LOG]{func.__name__}返回:{result}")returnresultreturnwrapper@log_function_calldefadd(a,b):returna+b add(3,5)权限检查装饰器
defrequire_admin(func):@wraps(func)defwrapper(user,*args,**kwargs):ifuser.get("role")!="admin":raisePermissionError("需要管理员权限")returnfunc(user,*args,**kwargs)returnwrapper@require_admindefdelete_user(user,user_id):returnf"用户{user_id}已删除"admin={"name":"Alice","role":"admin"}guest={"name":"Bob","role":"guest"}print(delete_user(admin,42))# 正常# delete_user(guest, 42) # 抛出 PermissionError缓存装饰器
fromfunctoolsimportlru_cache@lru_cache(maxsize=128)deffibonacci(n):ifn<2:returnnreturnfibonacci(n-1)+fibonacci(n-2)print(fibonacci(100))# 瞬间完成@functools.lru_cache是标准库提供的内存缓存装饰器,用于优化重复计算的递归函数。
4. 总结与最佳实践
- 迭代器是 Python 循环机制的核心,掌握
__iter__与__next__协议可以创建自定义可迭代对象。 - 生成器通过
yield提供了一种简洁的迭代器实现方式,惰性求值的特点特别适合处理大数据流。使用yield from能简化子生成器的委托。 - 装饰器本质上是对高阶函数和闭包的运用。它们能在不修改原函数代码的情况下添加切面功能,是 Python 最强大的元编程工具之一。
- 编写装饰器时务必使用
@wraps保留原始函数的元数据,否则调试和文档生成都会受影响。 - 对于带参数的装饰器,牢记三层函数结构:外层接收配置参数,中层接收原函数,内层是实际的包装逻辑。
- 当需要维护调用状态(如计数器)时,类装饰器可能更直观。
- 善用标准库中的装饰器,如
@property、@staticmethod、@classmethod、@functools.lru_cache等,它们已经为常见任务提供了最优方案。
