生成器
浏览 973
课文
什么是生成器
在写迭代器的时候我们每次都要定义一个 class
,实现其中的 __iter__
与 __next__
是不是略感麻烦。
之前返回一个小于等于指定值的正整数的迭代器我们就可以用生成器来这么写。
def positive(limit):
n = 1
while n <= limit:
yield n
n += 1
for n in positive(5):
print(n)
1
2
3
4
5
是不是感觉代码量一下子缩短了一大半。
使用了 yield
的函数便是生成器。
我们来看下生成器所实现了的方法有哪些。
...
generator = positive(5)
print(dir(generator))
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
一个 生成器
对象自然而然的就包含了 __iter__
与 __next__
,这意味着 生成器
必然就是一个 可迭代对象
与 迭代器
。这是一个从属关系,一个 可迭代对象
或 迭代器
不一定是 生成器
。
yield 关键字
我们都知道,获取 迭代器
的元素时要通过 next
方法,而 yield
关键字的作用就在于在每次执行 next
方法时执行到 yield
的位置返回值同时退回到 next
调用的位置继续执行。
看文字能学会代码的话我们还要键盘干嘛,来看一段代码。这个过程在交互模式下比较容易理解,需要动动手去实践一下。
def my_gen():
print('执行一次')
yield '第一次 yield'
print('执行两次')
yield '第二次 yield'
print('执行三次')
yield '第三次 yield'
print('执行四次')
gen = my_gen()
在第一次执行 next
函数时,会从 my_gen
函数开头执行到第一个 yield
关键字,期间打印了 执行一次
,返回了字符串 第一次 yield
到变量 result
,并回退到 next
执行的地方继续往下执行。
循环往复直到函数执行结束抛出 StopIteration
异常。
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']
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
使用生成器的场景
由于可以使用生成器很方便地实现一个迭代器,因此迭代器适用的场景生成器几乎都适用。
- 节省内存
- 流式处理数据
- 无限的数据
由于生成器可以通过 yield
与 send
中断执行并双向通信,因此生成器在 python 中被用来作为 协程
的一种实现,这部分知识到协程的章节将会着重介绍。
评论
暂无评论