? ? ? 生成器是一个特殊的程序,可以被用作控制循环的迭代行为,是一边循环一边计算的机制,称为generator 。
生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
使用场景:
? ? ? ? 由于可以使用生成器很方便地实现一个迭代器,因此迭代器适用的场景生成器几乎都适用。
1.实现generator的两种方式
第一种方法:把一个列表生成式的[]改成(),就创建了一个generator
#列表生成式
lis = [x*x for x in range(10)]
print(lis)
#生成器
generator_ex = (x*x for x in range(10))
print(generator_ex)
# 结果:
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# <generator object <genexpr> at 0x000002A4CBF9EBA0>
创建list和generator_ex的区别是什么呢?
? ? ? ? ?从表面看就是[? ]和(),但是结果却不一样,一个打印出来是列表(因为是列表生成式),而第二个打印出来却是<generator object <genexpr> at 0x000002A4CBF9EBA0>,
? ? ? 如何打印出来generator_ex的每一个元素呢?
#生成器
generator_ex = (x*x for x in range(10))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
结果:
0
1
4
9
16
25
36
49
64
81
Traceback (most recent call last):
File "列表生成式.py", line 42, in <module>
print(next(generator_ex))
StopIteration
可以看到,generator保存的是算法,每次调用next(generaotr_ex)就计算出他的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误,而且上面这样不断调用是一个不好的习惯,正确的方法是使用for循环,因为generator也是可迭代对象:
#生成器
generator_ex = (x*x for x in range(10))
for i in generator_ex:
print(i)
结果:
0
1
4
9
16
25
36
49
64
81
所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误,
第二种方式:在函数中使用yield关键字,函数就变成了一个generator。
? ? ? ? ? ? ? ? ? ? ?调用函数就是创建了一个生成器(generator)对象。
def positive(limit):
n = 1
while n <= limit:
yield n
n += 1
for n in positive(5):
print(n)
获取?迭代器 ?的元素时要通过?next ?方法,而?yield ?关键字的作用就在于在每次执行?next ?方法时执行到?yield ?的位置返回值同时退回到?next ?调用的位置继续执行。
2 yield from 关键字
# yield from 后面接一个 可迭代对象 ,等价于用 for in 去单独 yield。
list1 = [1, 2, 3, 4]
list2 = ['a', 'b', 'c', 'd']
def my_gen_1():
yield from list1
yield from list2
def my_gen_2():
for n in list1:
yield n
for n in list2:
yield n
print('my_gen_1 循环结果:', [x for x in my_gen_1()])
print('my_gen_2 循环结果:', [x for x in my_gen_2()])
# my_gen_1 循环结果: [1, 2, 3, 4, 'a', 'b', 'c', 'd']
# my_gen_2 循环结果: [1, 2, 3, 4, 'a', 'b', 'c', 'd']
3 send 方法实现数据的双向通信
通过?yield ?向生成器外部传值,实际上我们也可以向生成器内部传值
def double_money():
print('请输入你的金钱,我来负责乘2,(机会只有三次)')
# 需要先执行一次 send(None) 到这里
x = yield
x = yield x * 2
x = yield x * 2
x = yield x * 2
double = double_money()
# 必须先执行一次 send None
# 执行到第一个 yield 关键字
double.send(None)
print(double.send(10))
print(double.send(20))
print(double.send(30))
"""
请输入你的金钱,我来负责乘2,(机会只有三次)
20
40
60
"
def average_gen():
count = 0
n = yield
while True:
count += 1
n += yield n / count
av = average_gen()
av.send(None)
print(av.send(10))
print(av.send(20))
print(av.send(30))
"""
10.0
15.0
20.0
"""
|