Python 生成器

普通的函数

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值