什么是Iterators迭代器?
从概念上来说,Iterator就是Python的一种object,其中包含若干元素,同时这些元素可以被一个一个地读取;
从Python的具体实现来说,一个Iterator object需要实现 iterator protocol 迭代器协议,即实现 __iter__()
以及 __next__()
方法。
什么叫Iterable?
直翻过来,我们可以叫可迭代的,这个概念是针对Python中除了Iterator以外的object来说的。
一个Iterable的object,就是可以根据这个object来生成一个Iterator迭代器。
从实现上来说,iterable的object都内置了 __iter__()
方法。
有点绕晕了?不确定这两个方法是干什么用的?别急,我们来看一个for循环的例子:
>>> nums = [1, 2, 3, 4]
>>> for num in nums:
... print(num)
...
1
2
3
4
for item in [iterable] 是Python中的for循环,相信很多读者都会使用,但是其实Python在这里做了一件事:
- Python首先将[iterable]转换成一个 iterator: iterator = iter(iterable):
inter()
方法会调用 iterator 内置的__iter__()
方法来返回一个迭代器;
- 接着执行 item = iterator._next_();
如此循环,就可以遍历整一个数组,通过这个例子,我们就会更好的来说明一些概念;
- nums数组就是一个 iterable 的object:
- 其实现了
__iter__()
方法,而这个方法可以将当前的object转换成一个 iterator object
- 其实现了
- iterator实现了 iterator protocol, 因此可以执行
__next__()
方法来不断访问下一个元素,直到报出 StopIteration 异常;
现在我们可以总结一下:
Iterator,迭代器,就是一种实现了 iterator protocol 的object,实现了 __iter__()
以及 __next__()
两个方法;
Iterable的object,就是可以被 iter()
方法调用其 __iter__()
方法,将其转换为一个Iterator object的object:
- Sequences, 例如内置的lists, dictionaries, tuples, sets以及files;
- Sequences的特点就是可以用 integer index 来访问,例如array[0], 而generators就不可以;
- Generators, 生成器;
- 任意自定义的,实现了
__iter__()
的classes;
而python中的for循环, for item in [iterable] 就是:
- 先利用
iter()
将iterable object转换成 iterator object; - 接着使用 iterator.next() 不断获取元素;
可以换一种实现手法:
iterator = iter(iterable)
while item := iterator.__next__():
print(item)
结果如下:
>>> iterator = iter(nums)
>>> while item := iterator.__next__():
... print(item)
...
1
2
3
4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
什么是Generators?
在上一节的学习中,我们在总结Iterable object提到了 Generators 这个名词,顾名思义,生成器就是在循环迭代的过程中,动态地生成需要的元素;
这是什么意思呢?
我们继续用上面的例子,我们要循环遍历一个list,就需要将这个list转成 iterator 来进行循环,那么,如果这个list很大,所生成的需要在循环中所维护的内容也就会占据很大的内存空间,有些情况下效率就不能保证。
而Generators就可以很好的解决这个问题, generator 作为一种 iterable object,它在循环的过程中不需要保存所有的值,而是随取随用,是哟中惰性计算( lazy evaluation)。
创建Generators
我们有两种方法来创建一个 generators object:
-
创建一个 Generator Function 来返回一个 generator object:
>>> def create_gen(): ... n = 1 ... print('第一次被调用') ... yield n ... ... n += 1 ... print('第二次调用') ... yield n ... ... n += 1 ... print('第三次调用') ... yield n ... >>> gen = create_gen() >>> gen <generator object create_gen at 0x7fbbd8199ac0> >>> for item in gen: ... print(item) ... 第一次被调用 1 第二次调用 2 第三次调用 3
- 我们定义了一个方法 create_gen(), 其中包含了 yield 表达式,最终返回了一个 generator object:
- yield表达式与return类似,都会返回一个值,不同的是:
- return的返回伴随着方法的结束;
- 而yield在返回之后,这个被调用的方法将保存所有的状态,进入等待状态,等待下一次被调用;
- 我们可以说当一个方法内部出现 yield 表达式,这个方法就是一个 Generator Function;
- yield表达式与return类似,都会返回一个值,不同的是:
- 我们定义了一个方法 create_gen(), 其中包含了 yield 表达式,最终返回了一个 generator object:
-
第二种方法是使用 Generator Expression
>>> gen = (x*x for x in range(5))
>>> gen
<generator object <genexpr> at 0x7fbbd8199b30>
>>> for item in gen:
... print(item)
...
0
1
4
9
16
- Generator Expression与我们以前所使用的 List comprehension 很类似,区别在于list comprehension 列表解析外面包裹的是
[]
, 而这里使用的是()
- Generator Expression将会返回一个 generator object 来支持循环
为什么我们要使用Generators:
- 便于实现一个iterable object:比如我们的for循环现在需要一个 iterable object, 要求包含一串2的平方的数:
-
自定义的iterable类,就需要实现
__iter__()
以及__next__()
方法:class PowTwo: def __init__(self, max=0): self.n = 0 self.max = max def __iter__(self): return self def __next__(self): if self.n > self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result
-
而使用Generator就可以省力不少:
def PowTwoGen(max=0): n = 0 while n < max: yield 2 ** n n += 1
-
- 更高效地内存使用:
- 传统的内置
iterable object
例如list,tuple,在生成Iterators生成器时,需要将所有的数据放到iter()
方法中去,如果数据很大,内存不够了,就会很麻烦; - 而generator放在
iter()
中的仅仅只是一个 iterator object 并不会占用很多的内存空间
- 传统的内置
- 用来表示无穷的流:
-
我们知道,Generators在调用了 yield 之后就会保存所有的状态并进入等待状态;
-
那么就可以实现一个无限循环,只需要等待有需要的时候再调用就可以了,比如下面就是一个无限循环的递增生成器:
def gen(): n = 0 while True: yield n n += 1
-
- 嵌套使用Generators
-
比如我们要实现一个功能,将一组斐波那契数列(从数列第3项开始,每一项都等于前两项之和)的每个数都平方后求和:
def fibonacci_numbers(nums): x, y = 0, 1 for _ in range(nums): x, y = y, x+y yield x def square(nums): for num in nums: yield num**2 print(sum(square(fibonacci_numbers(10))))
-
拓展阅读
Reference
[1] e-satis, ‘Answer to “What does the ‘yield’ keyword do?”’, Stack Overflow, Oct. 23, 2008. https://stackoverflow.com/a/231855/17534765.
[2] user28409, ‘Answer to “What does the ‘yield’ keyword do?”’, Stack Overflow, Oct. 25, 2008. https://stackoverflow.com/a/237028/17534765.
[3] ‘Python Iterators (iter and next): How to Use it and Why?’ https://www.programiz.com/python-programming/iterator. [4] ‘Python 中生成器的原理 - zikcheng - 博客园’. https://www.cnblogs.com/zikcheng/p/16462284.html.
[5] ‘Python的generator_Wanderer001的博客-CSDN博客_generator函数 python’. https://blog.csdn.net/weixin_36670529/article/details/103941740.