生成器
在这一节我们学习有关 Python 生成器(Generators)的知识。生成器是更简单的创建迭代器的方法,这通过在函数中使用 yield
关键字完成:
>>> def my_generator():
... print("Inside my generator")
... yield 'a'
... yield 'b'
... yield 'c'
...
>>> my_generator()
<generator object my_generator at 0x7fbcfa0a6aa0>
在上面的例子中我们使用 yield
语句创建了一个简单的生成器。我们能在 for
循环中使用它,就像我们使用任何其它迭代器一样。
>>> for char in my_generator():
... print(char)
...
Inside my generator
a
b
c
在下一个例子里,我们会使用一个生成器函数完成与 Counter 类相同的功能,并且把它用在 for 循环中。
>>> def counter_generator(low, high):
... while low <= high:
... yield low
... low += 1
...
>>> for i in counter_generator(5,10):
... print(i, end=' ')
...
5 6 7 8 9 10
在 While 循环中,每当执行到 yield
语句时,返回变量 low
的值并且生成器状态转为挂起。在下一次调用生成器时,生成器从之前冻结的地方恢复执行然后变量 low
的值增一。生成器继续 while
循环并且再次来到 yield
语句...
当你调用生成器函数时它返回一个生成器对象。如果你把这个对象传入 dir()
函数,你会在返回的结果中找到 __iter__
和 __next__
两个方法名。
我们通常使用生成器进行惰性求值。这样使用生成器是处理大数据的好方法。如果你不想在内存中加载所有数据,你可以使用生成器,一次只传递给你一部分数据。
os.path.walk()
函数是最典型的这样的例子,它使用一个回调函数和当前的 os.walk
生成器。使用生成器实现节约内存。
我们可以使用生成器产生无限多的值。以下是一个这样的例子。
>>> def infinite_generator(start=0):
... while True:
... yield start
... start += 1
...
>>> for num in infinite_generator(4):
... print(num, end=' ')
... if num > 20:
... break
...
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
如果我们回到 my_generator()
这个例子,我们会发现生成器的一个特点:它们是不可重复使用的。
>>> g = my_generator()
>>> for c in g:
... print(c)
...
Inside my generator
a
b
c
>>> for c in g:
... print(c)
...
我们无法创建一个可重复使用的生成器,但可以创建一个对象,将它的 __iter__
方法调用得到一个生成器,举例如下:
>>> class Counter(object):
... def __init__(self, low, high):
... self.low = low
... self.high = high
... def __iter__(self):
... counter = self.low
... while self.high >= counter:
... yield counter
... counter += 1
...
>>> gobj = Counter(5, 10)
>>> for num in gobj:
... print(num, end=' ')
...
5 6 7 8 9 10
>>> for num in gobj:
... print(num, end=' ')
...
5 6 7 8 9 10
上面的 gobj
并不是生成器或迭代器,因为它不具有 __next__
方法,只是一个可迭代对象,生成器是一定不能重复循环的。而 gobj.__iter__()
是一个生成器,因为它是一个带有 yield 关键字的函数。
如果想要使类的实例变成迭代器,可以用 __iter__
+ __next__
方法实现:
>>> from collections import Iterator
>>> class Test():
...: def __init__(self, a, b):
...: self.a = a
...: self.b = b
...: def __iter__(self):
...: return self
...: def __next__(self):
...: self.a += 1
...: if self.a > self.b:
...: raise StopIteration()
...: return self.a
...:
>>> test = Test(5, 10)
>>> isinstance(test, Iterator)
True