Python——迭代器和生成器
引子:迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
迭代器
一、迭代器是什么
迭代器是一个可以记住遍历的位置的对象。
二、迭代器的特点
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器必须有的两个方法:iter() 和 next()。
三、哪些数据类型是可迭代的(iterable)?
字符串、列表、元组、字典、集合都是可迭代的。
四、代码演示
1.迭代器的用法
list = [1, 2, 3, 4] # 创建一个列表
it = iter(list) # 创建一个迭代器对象
print(next(it)) # 输出迭代器的下一个元素
print(next(it)) # 持续迭代
...
上面代码里,我们先创建了一个列表,然后把列表作为一个参数传给iter方法,生成一个迭代器对象it。再讲迭代器对象作为参数传给next方法,从而实现持续向下迭代。且每一次调用next,都会在列表里向后取一个值。如果超出列表的长度,则会报错 StopIteration。
2.模拟实现一个简单的迭代器
class MyIterator:
def __iter__(self): # 模拟实现 iter 方法
self.a = 1 # 实例属性,属于这个迭代器对象,对象不销毁,这个值就一直保存
return self # iter方法要求返回一个迭代器对象,这里是把自己这个实例对象当做迭代器返回
def __next__(self): # 模拟实现 next 方法
self.a += 1
if self.a > 20:
raise StopIteration # 主动抛出异常,防止无限循环——StopIteration 异常用于标识迭代的完成
return self.a
myclass = MyIterator() # 创建一个实例对象
myiter = iter(myclass) # 创建一个迭代器对象
while True:
print(next(myiter)) # 循环便利迭代器的下一个
借助讲解这个实现过程,顺便复习一下面向对象的知识。
形如“__iter__”这种格式的方法,在Python中为魔法方法,常见于类中初始化方法“__init__”。
self.a 是实例属性(对象变量)
self代表当前MyIterator的这个迭代器对象本身self.a就是绑定在这个对象身上的变量,用来记录迭代的当前状态(计数器)
普通局部变量是写在函数里面、函数调用完就消失;
self.a是挂在对象上,多次调用__next__的时候,值可以保留、持续累加,这是迭代器最关键的一点。
如果直接在方法里创建局部变量 a ,每次 __next__ 调用结束就销毁,记不住上一次数字,迭代器就没法工作。
__iter__ 要求返回一个迭代器对象; return self 就是:把自己这个实例对象,当做迭代器返回出去。 这里的 self = 当前 MyIterator() 创建出来的那个对象。
记住一条 Python 迭代器规则: 一个对象只要同时有
__iter__+__next__,它自己本身就是迭代器。
1. self 到底是什么?
当你写:
it = MyIterator()
it 就是这个对象,在类里面,self 就是这个 it 的别名。
self不是一个普通变量,它是自动传进来的第一个参数,代表当前实例self里面存着:- 绑定在它身上的实例属性:比如
self.a - 绑定在它身上的方法:
self.__next__()、self.__iter__()
- 绑定在它身上的实例属性:比如
你可以理解成: self 是一个小盒子,盒子里面装着我们这个迭代器需要的状态(计数器 a),还有它能执行的动作(__next__)。
self(盒子/对象)
├─ 属性:self.a
└─ 方法:__iter__、__next__
2. 为什么 iter 可以 return self?
迭代协议两条规则:
iter(对象)→ 调用对象的__iter__(),必须返回一个迭代器- 迭代器必须有
__next__()方法,next()函数会去调用它
MyIterator 实例刚好满足:它自己就带 __next__,它自己就是迭代器。 所以 __iter__ 直接把自己交出去:return self。
很多初学者会疑惑:为什么不新建一个别的对象返回? 当然也可以返回别的迭代器对象! 比如容器类(list、字典),
__iter__返回一个全新的迭代器,而不是自己。✅ 两种写法都合法:
- 方式 A(你现在这种:自迭代器):
__iter__ return self,自己既是可迭代对象,又是迭代器- 方式 B(容器常用):
__iter__返回一个独立的迭代器实例,容器本身不是迭代器
3.对比感受差别(自迭代器VS独立迭代器)
列表就是方式 B:
lst = [1,2,3]
i1 = iter(lst)
i2 = iter(lst)
print(i1 is lst) # False,返回新迭代器,列表本身不是迭代器
而我们的代码是方式 A:
it = MyIterator()
i1 = iter(it)
print(i1 is it) # True,自己就是迭代器
4. 重点坑点:自迭代器(return self)的副作用
因为可迭代对象和迭代器是同一个,它只有一份状态 self.a: 一旦迭代一遍跑完,self.a 到 20 抛出异常;再次 for 循环遍历它,不会重新从头开始!
it = MyIterator()
for x in it:
print(x)
# 再循环一次,直接空,不会重新从2开始
for x in it:
print(x)
原因:self.a 的状态已经停在 20 了,__iter__ 只有在你第一次 iter 的时候才会重置 self.a=1。
这就是为什么真实开发里,容器一般不采用
return self,而是返回独立迭代器。
5. 总结极简版
self:当前实例对象,里面保存实例属性(self.a)和方法__iter__的任务:交出一个迭代器return self的含义:我自己就是迭代器,直接把我交给 for 循环 /next 函数去使用- 前提:这个对象必须实现
__next__,否则交出去也没法 next ()
生成器
待补充……

参考文章:

浙公网安备 33010602011771号