学前提示:请务必先阅读了解『迭代器』后,再阅读本篇文章。因为生成器对象本身就是一种特殊的迭代器。
➊ 生成器函数:函数体中如果出现了yield关键字,那该函数是『生成器函数』。不管能否执行到yield所在的位置,只要函数中有yield,那该函数就是『生成器函数』。
➋ 生成器对象:调用『生成器函数』时,其函数体不会立刻执行,而是返回一个『生成器对象』。
def demo():
print('demo函数开始执行了……')
print(100)
yield
a = 200
print(a)
d = demo()
print(d) # <generator object demo at 0x000001FC96545B40>
写在『生成器函数』中的代码,需要通过『生成器对象』来执行:
❏ 调用『生成器对象』的__next__()方法,会让『生成器函数』中的代码开始执行。
❏ 当『生成器函数』中的代码开始执行后,遇到yield会“暂停”,并会记录“暂停”的位置。
❏ 后续调用__next__()方法时,都会从上一次“暂停”的位置,继续运行,直到再次遇到yield。
❏ 遇到return会抛出StopIteration异常,并将return后面的表达式,作为异常信息。
❏ yield后面所写的表达式,会作为本次__next__()方法的返回值。
def demo():
print('demo函数开始执行了')
print(100)
yield '我是第一个yield所返回的数据'
a = 200
print(a)
yield '我是第二个yield所返回的数据'
b = 300
print(b)
return '生成器异常'
d = demo()
r1 = next(d)
print(r1)
r2 = next(d)
print(r2)
try:
next(d)
except StopIteration as e:
print(e)
# 运行结果:
# demo函数开始执行了
# 100
# 我是第一个yield所返回的数据
# 200
# 我是第二个yield所返回的数据
# 300
# 生成器异常
生成器对象是一种特殊的迭代器(本质是通过yield自动实现了迭代器协议)。
def demo():
print('demo函数开始执行了')
print(100)
yield '我是第一个yield所返回的数据'
a = 200
print(a)
yield '我是第二个yield所返回的数据'
b = 300
print(b)
return '生成器异常'
d = demo()
# 验证:生成器对象d,和迭代器一样,也拥有:__iter__ 和 __next__ 方法
print(hasattr(d, '__iter__')) # True
print(hasattr(d, '__next__')) # True
# 验证:生成器对象的__iter__方法,和迭代器一样,返回的也是自身
result = iter(d)
print(result == d) # True
# for循环遍历生成器
for item in d:
print(item)
# for循环背后的逻辑
d2 = demo()
gen = iter(d2)
while True:
try:
value = next(gen)
print(value)
except StopIteration:
break
yield也能写在循环里。
def create_car(total):
for index in range(1, total + 1):
yield f'下线了第{index}台车'
# 创建迭代器对象
cars = create_car(5)
print(cars)
# 调用一次cars的__next__方法,就会得到一台车
c1 = next(cars)
print(c1)
c2 = next(cars)
print(c2)
c3 = next(cars)
print(c3)
c4 = next(cars)
print(c4)
c5 = next(cars)
print(c5)
# 第6次执行就会抛出StopIteration异常
# c6 = next(cars)
# print(c6)
# 使用for循环遍历
cars2 = create_car(5)
for car in cars2:
print(car)
yield from能把一个『可迭代对象』里的内容依次yield出去。(替代:for循环 + yield)
def demo():
nums = [10, 20, 30, 40, 50]
yield from nums
d = demo()
r1 = next(d)
print(r1)
r2 = next(d)
print(r2)
r3 = next(d)
print(r3)
r4 = next(d)
print(r4)
r5 = next(d)
print(r5)
d2 = demo()
for x in d2:
print(x)
使用生成器.send(值)可以让生成器继续执行的同时,给上一次yield传值。
注意:
next只能取值,send既能取值,也能送值。第一次启动生成器,不能传值!(或者说只能传None值)
def demo():
print('demo函数开始执行了')
print(100)
a = yield '我是第一个yield所返回的数据'
print(f'_ {a}')
b = yield '我是第二个yield所返回的数据'
print(f'_ {b}')
return '生成器异常'
d = demo()
r1 = next(d)
print(r1)
r2 = d.send(666)
print(r2)
try:
d.send(888)
except StopIteration as e:
print(e)
# 运行结果:
# demo函数开始执行了
# 100
# 我是第一个yield所返回的数据
# _ 666
# 我是第二个yield所返回的数据
# _ 888
# 生成器异常
➊ 用生成器实现遍历Person类的实例对象:
class Person:
def __init__(self, name, age, gender, address):
self.name = name
self.age = age
self.gender = gender
self.address = address
self.__attr = [name, age, gender, address]
def __iter__(self):
# yield self.name
# yield self.age
# yield self.gender
# yield self.address
yield from self.__attr
p1 = Person('远方', 41, '男', '山西太原')
for attr in p1:
print(attr)
# 运行结果:
#远方
#41
#男
#山西太原
➋ 用生成器实现斐波那契数列:
def fibo(total):
pre = 1
cur = 1
for index in range(total):
if index < 2:
yield 1
else:
value = pre + cur
pre = cur
cur = value
yield value
f1 = fibo(10)
for item in f1:
print(item)
# 运行结果:
# 1
# 1
# 2
# 3
# 5
# 8
# 13
# 21
# 34
# 55
无论是迭代器,还是生成器对象,都可以用list、tuple、set等直接拿到其里面的所有内容(注意:如果数据量很大,可能会挤爆内存)。
def fibo(total):
pre = 1
cur = 1
for index in range(total):
if index < 2:
yield 1
else:
value = pre + cur
pre = cur
cur = value
yield value
f1 = fibo(10)
result = tuple(f1)
print(result) # (1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
生成器表达式:一种用类似列表推导式的语法,快速创建生成器对象的方式。
语法格式:(表达式 for 变量 in 可迭代对象)
什么时候适合用生成器表达式?—— 当“每个结果,只依赖当前这一个元素”时。
nums = [10, 20, 30, 40, 50]
# 列表推导式
result1 = [n * 2 for n in nums]
print(result1)
# 生成器表达式和列表推导式很像,不要混淆
result2 = (n * 2 for n in nums)
print(result2) # <generator object <genexpr> at 0x000002899789A8E0>
for n in nums:
print(n)