一、生成器
在python中,生成器是一种特殊的迭代器,内部支持生成器协议,不需要明确定义__iter__()和__next__()方法。
生成器通过生成器函数产生,生成器函数可以通过常规的def语名来定义。
函数内包含有yield关键字,调用函数就不会执行函数体代码,拿到的返回值就是一个生成器对象。
生成器本质就是迭代器,也就是说生成器的操作法其实就是迭代器一样。
yield作用:
1、为我们提供了一种自定义迭代器的方式,
可以在函数内用yield关键字,调用函数拿到的结果就是一个生成器,生成器就是迭代器。
2、yield可以像return一样用于返回一个值,区别是return只能返回一次值,但yield可返回多次值.因为yield可以保存函数执行的状态。
def MyRange(n):
i=0
while i<n:
yield i
i+=1
myrange=MyRange(5)
print(myrange)
print([i for i in myrange])
# 运行结果
# <generator object MyRange at 0x0000000001EB7678>
# [0, 1, 2, 3, 4]
在上面的例子中,我们定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语名进行迭代访问。
现在我们来探讨一个生成器函数的执行流程。我们稍微丰富一下上面的例子:
def MyRange(n):
print('开始执行这个生成器函数')
i=0
while i<n:
print('yield 执行前',i)
yield i
i+=1
print('yield 执行后',i)
print('生成器函数执行结束')
myrange=MyRange(5)
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
执行结果:
-------------------
开始执行这个生成器函数
yield 执行前 0
0
-------------------
yield 执行后 1
yield 执行前 1
1
-------------------
yield 执行后 2
yield 执行前 2
2
-------------------
yield 执行后 3
yield 执行前 3
3
-------------------
yield 执行后 4
yield 执行前 4
4
-------------------
yield 执行后 5
生成器函数执行结束
Traceback (most recent call last):
File "D:/WorkSpace/每日面试/生成器.py", line 36, in <module>
print(myrange.__next__())
StopIteration
·当调用生器函数的时候,函数只返回了一个生成器对象,并没有执行。
·当__next__()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处暂停。__next__()方法的返回值就是yield语名处的参数
·当继续调用__next__()方法的时候,函数将接着一一次停住的yield语外继续执行,到下一个yield处停止,循环直到后面没有yield就抛出StopIteration异常。
生成器表达式
了解生成器表达式,我人首先要知道列表解析式:
[ iter_var for iter_var in iterable if cond_expr]
迭代iterable里所有内容,每一次迭代后,把iterable中满足cond_expr条件的内容放到iter_var中.
例如:生成一个 列表来保存100内能被5整除的数
a=[i for i in range(100) if i%5==0]
print(a)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
生成器表达示是python2.4以后引入的,当序列过长,每次中需要获取一个元素时,就应当考虑使用生成器表达式。生成器表达式的语法和列表解析式相类似,只不过生成器表达式不是被[]括起来的,而是用()括起来。
(iter_var for iter_var in iterable if cond_expr)
我们将上面的例子改一下:
a=(i for i in range(100) if i%5==0)
print([i for i in a])
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
生成器表达示是返回一个生成器,这个生成吕每次计算出一个条目后,把这个条目yiled出来,生成器表达式用了惰性计算,只在在检索进才被赋值,所以在列表比较长的情况下使用它,能够也省内存。
a=(i for i in range(100) if i%5==0)
print([i for i in a])
print([i for i in a])
print('__iter__'in dir(a))
print('__next__'in dir(a))
#运行结果
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
[]
True
True
从上面我们可以看出,生成器表达式产生在生成器,即是可迭代对象,也是迭代器本身。
递归生成器
生成器可以像函数一个进行递归使用。我们对一个序列进行全排序:
def permutations(li):
if len(li) == 0:
yield li
else:
for i in range(len(li)):
li[0], li[i] = li[i], li[0]
for item in permutations(li[1:]):
yield [li[0]] + item
for item in permutations(range(3)):
print item
生成器的send和close()方法
send()
从前面我们了解到__next__()方法可恢复生成器的状态并继续执行,而send()也是一个恢复成生器的方法。
在python2.5后yield语名变成了yield表达式,也就是说yield可以有一个值,而这个值就是send()方法的参数,send(None)和__next__()是等效的。
close()
这个方法用于关闭生成器,对关闭的生成器后再调用send()或__next__()方法会抛出StopIteration异常。
看看下面的使用例子:
def MyRange(n):
print('开始执行这个生成器函数')
i=0
while i<n:
print('yield 执行前',i)
val=yield i
i+=1
print('yield 执行后',i,val)
print('生成器函数执行结束')
myrange=MyRange(5)
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.__next__())
print('-------------------')
print(myrange.send('hello'))
print('-------------------')
print(myrange.close())
print('-------------------')
print(myrange.__next__())
执行结果:
-------------------
开始执行这个生成器函数
yield 执行前 0
0
-------------------
yield 执行后 1 None
yield 执行前 1
1
-------------------
yield 执行后 2 hello
yield 执行前 2
2
-------------------
None
-------------------
Traceback (most recent call last):
File "D:/WorkSpace/每日面试/生成器.py", line 34, in <module>
print(myrange.__next__())
StopIteration
总结:
生成器是一种特殊的迭代器,内部支持了生成器协议,不需要明确定义__iter__()和__next__()方法。
生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果。