ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python.二.(三).迭代器和生成器

Python.二.(三).迭代器和生成器 Python 中迭代器的概念核心结论可迭代对象Iterable实现__iter__迭代器Iterator实现__iter____next__。迭代器是带状态的惰性求值对象 。我们来看一个from collections import Iterable print(isinstance(abcddddd, Iterable)) # str是否可迭代 print(isinstance([1,2,3,4,5,6], Iterable)) # list是否可迭代 print(isinstance(12345678, Iterable)) # 整数是否可迭代 -------------结果如下---------------- True True False当对所有的可迭代对象调用 dir() 方法时会发现他们都实现了 iter 方法。这样就可以通过 iter(object) 来返回一个迭代器。x [1, 2, 3] y iter(x) print(type(x)) print(type(y)) ------------结果如下------------ class list class list_iterator可以看到调用iter()之后变成了一个list_iterator的对象。可以发现增加了一个__next__方法。所有实现了__iter__和__next__两个方法的对象都是迭代器。迭代器是带状态的对象它会记录当前迭代所在的位置以方便下次迭代的时候获取正确的元素。__iter__返回迭代器自身__next__返回容器中的下一个值如果容器中没有更多元素了则抛出Stoplteration异常。x [1, 2, 3] y iter(x) print(next(y)) print(next(y)) print(next(y)) print(next(y)) ----------结果如下---------- 1 2 3 Traceback (most recent call last): File /Users/Desktop/test.py, line 6, in module print(next(y)) StopIteration如何判断对象是否是迭代器和判断是否是可迭代对象的方法差不多只要把 Iterable 换成 Iterator。Python的for循环本质上就是通过不断调用next()函数实现的举个栗子下面的代码先将可迭代对象转化为Iterator再去迭代。这样可以节省对内存因为迭代器只有在我们调用 next() 才会实际计算下一个值。可迭代对象是迭代器、生成器和装饰器的基础。简单来说可以使用for来循环遍历的对象就是可迭代对象。比如常见的list、set和dict一、两个概念Iterable ≠ Iterator概念协议作用可迭代对象Iterable__iter__能被for遍历迭代器Iterator__iter____next__带状态逐个产出元素from collections.abc import Iterable, Iterator x [1, 2, 3] isinstance(x, Iterable) # True list 是可迭代对象 isinstance(x, Iterator) # False 但不是迭代器 y iter(x) # iter() 把 Iterable 转为 Iterator isinstance(y, Iterator) # True补充有__getitem__且接受整数索引的对象即使没有__iter__也能被iter()使用旧协议向后兼容。二、迭代器的工作机制迭代器是带状态的对象记录当前迭代位置。__iter__返回自身__next__返回下一个值无元素时抛出StopIterationx [1, 2, 3] y iter(x) print(next(y)) # 1 print(next(y)) # 2 print(next(y)) # 3 print(next(y)) # StopIteration三、for 循环的本质Python 的for循环等价于以下代码# for elem in x: 等价于 ↓ _iter iter(x) while True: try: elem next(_iter) # 循环体 except StopIteration: break先调用iter()转为迭代器再不断next()捕获StopIteration退出。迭代器惰性求值——只有调用next()才计算下一个值节省内存。四、生成器与迭代器的关系生成器Generator是创建迭代器的便捷方式自动实现迭代器协议# 生成器函数用 yield def counter(start0): while True: yield start start 1 # 生成器表达式 squares (x**2 for x in range(10))生成器本身就是迭代器无需手写__iter__/__next__。五、itertools 实用工具from itertools import count, cycle, chain, islice # count: 无限计数器 c count(start13) next(c) # 13 next(c) # 14 # chain: 拼接多个可迭代对象 list(chain([1, 2], [3, 4])) # [1, 2, 3, 4] # islice: 切片用于无限迭代器 list(islice(count(1), 5)) # [1, 2, 3, 4, 5]一句话记忆Iterable 是能被遍历的Iterator 是带状态逐个产出的。iter()把前者变成后者for循环 iter()while next()StopIteration。生成器是创建迭代器的语法糖。. Python 中生成器的相关知识结论生成器是惰性求值的迭代器用yield暂停执行并产出值。现代协程已不再依赖 generator而是async/await核心价值不必一次性生成所有数据按需计算节省内存。# 列表推导式定义即生成占满内存 a [x * x for x in range(10)] # 生成器表达式惰性求值几乎不占内存 b (x * x for x in range(10))生成器函数用yield代替return遇到yield暂停并返回值下次next()从断点恢复def spam(): yield first yield second yield third for x in spam(): print(x) # first / second / third调用函数不执行函数体而是返回生成器对象。yield记录执行位置下次从断点继续。send()— 双向通信生成器不仅能产出值还能接收外部传入的值def echo(): while True: received yield # 接收 send() 传入的值 print(fgot: {received}) gen echo() next(gen) # 先启动到 yield 处暂停必须 gen.send(hello) # got: hello gen.send(world) # got: worldyield from— 委托生成器PEP 380def sub_gen(): yield 1 yield 2 def main_gen(): yield 0 yield from sub_gen() # 直接委托给子生成器 yield 3 list(main_gen()) # [0, 1, 2, 3]yield from建立主生成器与子生成器的双向通道是async/await的前身。协程的现代演进⚠️阶段机制状态早期generator yield已废弃PEP 380yield from仍可用于生成器委托PEP 492async/await✅ 当前标准# 现代协程写法 import asyncio async def fetch_data(): await asyncio.sleep(1) return data❌ 协程通过 generator 实现是过时说法。现代协程用async/awaitasyncio.coroutine装饰器已在 3.12 移除。. Python 中装饰器的相关知识结论装饰器本质是接收函数、返回函数的高阶函数用语法糖实现隐式包装。必须加functools.wraps保留元信息 从手动包装到语法糖的演进import logging from functools import wraps def use_log(func): wraps(func) # ✅ 保留原函数 __name__、__doc__ 等元信息 def wrapper(*args, **kwargs): logging.warning(%s is running % func.__name__) return func(*args, **kwargs) return wrapper use_log # 等价于 bar use_log(bar) def bar(): I am bar print(I am bar) bar() # WARNING:root:bar is running # I am bar bar.__name__ # bar不加 wraps 会变成 wrapper带参数的装饰器— 三层嵌套def repeat(times): # 第 1 层接收参数 def decorator(func): # 第 2 层接收被装饰函数 wraps(func) def wrapper(*args, **kwargs): for _ in range(times): result func(*args, **kwargs) return result return wrapper return decorator repeat(3) # 先调用 repeat(3) 返回 decorator再用 decorator 装饰 def greet(): print(hi) greet() # 打印 3 次 hi类装饰器— 装饰器不一定是函数也可以是类class CountCalls: def __init__(self, func): self.func func self.count 0 def __call__(self, *args, **kwargs): self.count 1 print(fcall #{self.count}) return self.func(*args, **kwargs) CountCalls def say_hi(): print(hi)多个装饰器的执行顺序decorator_a # 先装饰外层 decorator_b # 后装饰内层 def func(): pass # 等价于func decorator_a(decorator_b(func)) # 执行时a 先进入b 后进入b 先退出a 后退出洋葱模型常见应用场景日志记录、性能计时、权限校验、缓存functools.lru_cache、重试机制。一句话记忆生成器用yield做惰性求值协程已转向async/await装饰器必须加wraps记住接收函数返回函数这一个本质带参装饰器只是多套一层。
返回列表