python中的迭代器与生成器
迭代器:
两个基本的方法:iter()和next()
- __iter__() 方法返回一个特殊的迭代器对象, 这
- 个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
- __next__() 方法会返回下一个迭代器对象。
核心区分
class MyNumbers: def __iter__(self): self.a = 1 return self # 返回自己,作为迭代器 def __next__(self): x = self.a self.a += 1 return x myclass = MyNumbers() it = iter(myclass) # 执行 __iter__,self.a=1,返回对象self print(it) # 打印对象:<__main__.MyNumbers object at 0x...> print(next(it)) # 调用 __next__ → 1 print(next(it)) # 调用 __next__ → 2iter(x)→ 调用x.__iter__(),得到迭代器对象print(iter(myclass)) # 输出类似:<__main__.MyNumbers object at 0x0000021xxxx>打印出来的是对象描述、内存地址,不会执行next,不会产生 1、2、3 这种数据。
执行iter(myclass)的时候:
- 会跑
__iter__里面代码:self.a =1✅(状态初始化发生在这里!) return self返回实例对象- 但是不会调用
__next__,不会计算、不会返回数字 - 这一步打印不会输出业务数据(数字、列表这些),它返回的是迭代器对象本身,打印对象看到的是对象内存信息,不是迭代产出的值。
next(迭代器对象)→ 调用__next__(),才拿到实际的元素值it = iter(myclass) print(next(it)) # 输出 1
📌一句话记忆:iter()负责造迭代器工具;next()负责驱动工具产出数据。
总结表格
| 代码 | 执行的方法 | 返回什么 | print 之后看到什么 |
|---|---|---|---|
iter(x) | x.__iter__() | 迭代器对象 | 对象内存信息,没有业务数据 |
next(it) | it.__next__() | 迭代出来的元素 | 真实数据,比如1,2,3 |
易错点提醒:
__iter__只是做初始化、返回迭代器;真正产生每一个值,是交给next/ next()。就算
__iter__内部初始化了self.a=1,这个 1 只是变量状态,不会自动跑出来,必须等next()调用__next__才会取出来返回。
生成器函数(带 yield):
只要 Python 看到函数定义体内存在 yield,它就把这个函数标记为生成器函数。
def gen(): yield 10 g = gen() # 等价于 iter(...) 的效果,拿到生成器迭代器对象。生成器自动实现iter和next,简化代码 print(g) # <generator object gen at 0x...>,打印对象,拿不到10 print(next(g)) # 10,next才会拿到产出的值运行实例:
def gen(): print("开始执行函数内部") yield 100 g = gen() # 第0步,仅仅构造生成器,返回一个生成器对象,这个对象处于休眠状态,需要next唤醒,并没有执行函数 print("这里是g = gen()之后")#打印结果 print(next(g))#激活对象之后,开始执行函数,根据函数定义先打印"开始执行函数内部",再通过yield输出100调用生成器函数时:
- 不执行函数里面任何一行代码
- 直接返回一个「生成器对象」,保存函数的入口位置、局部变量的上下文
- 这个对象处于休眠状态,等着 next() 来唤醒
- yield 不是 return。
- yield 是暂停指令,它生效的前提是代码执行流走到这一行。 没被 next 唤醒,代码流根本走不到 yield a 那一行,自然不可能输出 a。
#以下是执行结果 这里是g = gen()之后 开始执行函数内部 100区分两件事(极易混淆):
- 函数定义里面写了 yield:只是告诉 Python,这是生成器。此时什么都不发生。
- 程序执行流运行到 yield 这行语句:才会暂停 + 向外产出值。
写在代码里 ≠ 程序已经跑到这行执行了。
一句话记住规则
- 写
yield a≠ 立刻把 a 算出来返回 yield a只是定义了一个「将来被 next () 调用时,要产出什么值」。- 函数只有遇到 next () /for 循环才会真正跑内部代码。