Python中一边循环一边计算的机制称为生成器,即generator。在generator中记录了获取下一个对象的算法,当我们需要数据的时候就可以通过生成器去获取,而不需要事先存储这些数据,以免浪费资源。
一、创建生成器的两种方法
(1)使用"()"来生成,将列表推导式中的"[ ]"改为"()"即可
# 创建生成器对象nums
nums = (x for x in range(5))
print("nums的类型:", type(nums))
# 输出: <class 'generator'>
# 遍历生成器,使用next() 或者 for...in...
# next() 方法一次获取一个,当取到最后一个之后,再次获取抛出异常:StopIteration
print(next(nums))
# 输出: 0
for i in nums:
print(i)
# 输出: 0 1 2 3 4
(2)使用函数来实现生成器,在函数需要的地方使用yield,这样函数就变成了一个生成器对象,此时调用函数的返回值就是一个生成器对象,如下示例:
# 通过函数实现生成器对象
def generator_e():
x = 2
while True:
y = 3 * x + 1
x = y
yield y # 将函数改变为一个生成器对象
# 调用函数,则函数返回值是一个generator
var = generator_e()
print("var的类型:", type(var))
# 输出:var的类型: <generator object generator_e at 0x000001DD4C77AB88>
# 对该对象同样可以使用next()和for...in...来遍历(二者区别是:若是无数量限制,for循环会
# 一直往下执行)
print(next(var))
i = 0
for item in var:
i += 1
if i == 6:
break
print(item)
二、生成器中的yield的执行原理
如果是第一次对生成器使用next(),则从def 函数代码块的部分开始执行,直到遇到yield时停止,并且把yield关键字后的数值当做调用next()的返回值,如果不是第一次,则从上一次暂停的位置继续往下执行直到遇到下一次yield为止,同样把yield关键字后的数值返回,如下示例代码:
def generator_i():
x = 1
count = 1
while True:
y = 3 * x + 1
x = y
print("yield 执行前,第%d次调用next函数:"%count)
yield y # 将函数改变为一个生成器对象
count += 1
print("yield 执行, 此时count的值为: %d"%count)
var = generator_i()
print(next(var))
>> yield 执行前,第1次调用next函数:
>> 4
print(next(var))
>> yield 执行, 此时count的值为: 2
>> yield 执行前,第2次调用next函数:
>> 13
print(next(var))
>> yield 执行, 此时count的值为: 3
>> yield 执行前,第3次调用next函数:
>> 40
三、生成器中send()方法
从前面可知,我们可以通过next()方法让生成器继续往下执行,获取下一个元素。在Python中,我们还可以使用send()方法来让生成器往下执行,send()方法的另一个优点是让我们主动向生成器发送数据。使用send()方法的基本格式是:xxx = yield yyy(只需将yield 语句"赋值"给某个对象即可,执行过程中,send会让生成器从上次停止的位置继续往后执行,并且会将传入的参数传递到生成器中)
def generator_m():
while True:
print("***** *_* *****")
strf= yield 100 # 此处可以通过send()传值,如果不传值则下一次
# 开始执行会将None赋值给strf
print("*——*","strf = ", strf)
g = generator_m() # 生成器对象
print(next(g))
>> ***** *_* *****
>> 100
print(g.send("第一次调用send方法给num传值")) # 将这个值传递给strf参数,并
# 继续往后执行直到遇到yield
>> *——* strf = 第一次调用send方法给num传值
>> ***** *_* *****
>> 100
print(next(g))
>> *——* strf = None
>> ***** *_* *****
>> 100
需要注意的是如果是第一次使用生成器,不能直接使用send(参数)方式来获取下一个数据,需要使用next()或者使用send(None)启动生成器,如下例:
def generator_n():
while True:
print("* *_* **")
strf = yield 100
print("*——*", "strf = ", strf)
n_n= generator_n()
# 直接传参或者不传参
n_n.send("00000"
>> TypeError: can't send non-None value to a just-started generator
>> TypeError: send() takes exactly one argument (0 given)
# 传参None 或者 使用 next(n_n)
print(n_n.send(None))
>> * *_* **
>> 100
# 继续传参
print(n_n.send(1))
>> *——* 1
>> * *_* **
>> 100