生成器是什么?
做循环的时候,我们常常会先获取一个列表,用for语句去循环这个列表当中的元素。但是有时候列表的获取会受到限制,例如创建一个包含100万个元素的列表,会占用很大的存储空间,再比如有时我们只需要访问列表当中的前几个元素,不需要将列表完全生成出来。在这种受到限制的情况下,我们可以用生成器来解决问题。
在Python中,这种一边循环一边计算的机制,称为生成器。通过一边循环一边计算可以节省内存空间。
生成器的创建方法
方法一: 用小括号+for的表达式,这是生成器创建最简单的方法
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
方法二: 生成器函数
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个生成器。函数运行时每次执行到一个yield
,就会暂停,生成一个值给函数的调用者。当下一次回到这个函数时,将从上次暂停处继续执行。当执行到函数末尾或者return
语句时,抛出StopIteration
的异常,return的值保存在StopIteration
异常对象的value
字段中。
用这种形式接受生成器函数的值,得到的是一个生成器对象,而不是生成器函数生成的值:
def Gen():
for i in range(5):
yield i
a = Gen()
print(a)
打印的结果
正确的方法如下:
for i in a:
print(i)
生成器的使用
方法一:可以用next
函数,每次调用返回下一个结果。如果到了最后一个元素,将抛出StopIteration
的异常。
方法二:用for循环。需要注意的是用for循环无法获取return的结果。如果需要return的结果,要用while循环。
迭代器是什么?
可以用next
读取下一个对象的类型统称为迭代器Iterator
。因此生成器也是一种迭代器。
集合数据类型(例如list
set
等)不是Iterator
,而是可迭代类型Iterable
。可迭代类型Iterable
指的是可以用iter
函数转为迭代器的类型。iter
函数的原型是类当中预定义的__iter__
函数,因此可迭代类型也可以理解为是定义了__iter__
函数的类型。这个函数在for循环当中会被隐式调用。例如
for x in [1, 2, 3, 4, 5]:
pass
这里就隐式调用了__iter__
方法。
我们可以给类自定义__iter__
函数,将这个类的对象变成迭代器,从而在for当中调用。
__iter__
和__next__
函数的差别
__iter__
函数在for循环的开始处调用,生成一个支持next
函数的对象,在for循环中对这个对象执行next
操作。
__next__
定义了对象在直接被执行了next
时的表现。
比如这个例子:
class IterTest:
def __init__(self):
self.IterList = [1, 2, 3, 4, 5]
self.NextList = iter([6, 7, 8, 9, 10])
def __next__(self):
print("执行了__next__函数")
return self.NextList.__next__()
def __iter__(self):
print("执行了__iter__函数")
return iter(self.IterList)
if __name__ == '__main__':
it = IterTest()
print('next函数调用的------')
while True:
try:
a = next(it)
print(a)
except StopIteration:
break
print('for循环调用的------')
for i in it:
print(i)
输出:
可以看出在for循环中调用了__iter__
,输出了self.IterList
这个序列的值,在next函数中调用了__next__
,输出了self.NextList
的值