普通的函数
def func():
print(123)
return 1234
v1 = func()
print(v1)
输出结果:
123
1234
生成器函数
如果我们在函数里面添加yield
关键字,这个函数就成了生成器函数。例如:
def func():
print(111)
yield 1
如果有多个yield
关键字,同样也是生成器函数:
def func():
print(111)
yield 1
print(222)
yield 2
print(333)
yield 3
print(444)
当我们执行生成器函数时,并不会像普通函数一样执行函数,而是返回一个生成器对象:
def func():
print(123)
yield 1234
# 函数体默认不会被执行, 返回生成器对象。
v1 = func()
print(v1)
输出结果:
<generator object func at 0x000001FF2D866960>
甚至连函数体内的print(123)
也没有打印,而是直接返回了一个生成器对象。
执行生成器函数
使用next
,可以进入生成器函数,并执行其中的代码。
def func():
print(123)
yield 1234
v1 = func()
# 进入生成器函数
next(v1)
输出结果:
123
可以看出执行了生成器函数体里面的内容并输出了结果。
如果有多个yield
关键字的情况下,会怎么输出:
def func():
print(123)
print(123)
yield 1234 # 有点像`return`,当执行到这个位置之后,就不再执行了
print(456)
yield 5678
print(789)
v1 = func()
n1 = next(v1)
print(n1)
输出结果为:
123
123
1234
说明next
执行了生成器函数体,但是并没有输出print(456)
和print(789)
,说明遇到yield
关键字之后,就停止继续执行了。
而当我们再次执行next(v1)
时,会从上次yield
返回位置继续向下。
def func():
print(123)
yield 1234
print(456)
yield 5678
print(789)
v1 = func()
n1 = next(v1)
print(n1)
# 从上一次yield位置,继续向下执行
n2 = next(v1)
print(n2)
输出结果:
123
1234
456
5678
其中123
是函数体中的输出,1234
是第一个yield
的输出结果,456
是继续执行next(v1)
的函数体内容,5678
是第二个yield
的输出结果。
如果此时我们在执行一个next(v1)
,就会报错,因为函数体已经执行完成了:
StopIteration
基于for循环执行生成器
一般情况下我们都是通过for
循环来执行生成器对象的。本质上在内部会帮我们遍历执行next()
def func():
print(123)
yield 1234
print(456)
yield 5678
print(789)
data = func()
# 会将yield的返回值赋值给item
for item in data:
print(item)
输出结果为:
123 # 函数体输出
1234 # yield返回结果赋值给item,并输出
456 # 函数体输出
5678 # yield返回结果赋值给item,并输出
789 # 函数体输出
通过for
循环,执行生成器不会遇到StopIteration
的错误。
应用场景
假设要让你生成 300w个随机的4位数,并打印出来。
- 在内存中一次性创建300w个
- 动态创建,用一个创建一个。
import random
data_list = []
for i in range(300000000):
val = random.randint(1000, 9999)
data_list.append(val)
# 再使用时,去 data_list 中获取即可。
# ...
使用生成器:
import random
def gen_random_num(max_count):
counter = 0
while counter < max_count:
yield random.randint(1000, 9999)
counter += 1
data_list = gen_random_num(3000000)
# 再使用时,去 data_list 中获取即可。
使用生成器的情况下,可以边使用边生成,节省了很大的内存空间。
send
上面我们介绍了通过next
可以执行生成器,也可以通过for
循环遍历生成器对象。
我们还可以通过send
来执行生成器。
def func():
print(111)
v1 = yield 1
print(v1)
print(222)
v2 = yield 2
print(v2)
print(333)
v3 = yield 3
print(v3)
print(444)
data = func()
n1 = data.send(None) # 第一次传递参数必须是None, yield执行结果返回给n1
print(n1)
n2 = data.send(666) # send的内容赋值给v1, yield执行结果返回给n2
print(n2)
n3 = data.send(777)
print(n3)
输出结果:
111
1
666
222
2
777
333
3
888
444