python yield

# example 1.1

def func():
    return 1

def gen():
    yield 1

print(type( func )) # <class 'function'>
print(type( gen )) # <class 'function'>

print(type( func() )) # <class 'int'>
print(type( gen() )) # <class 'generator'>

从例 1.1 可以看到,funcgen都是函数, 但前者返回数字 1,后者返回的是个生成器(generator)。

# example 1.2

print(type( (i for i in range(10)) )) # <class 'generator'>

其实平常接触更多的是像例 1.2 这样的生成器, 由一个生成器表达式(generator expression)生成。

但两者是不同的东西,看下面这个例子。

# example 1.3

gen1 = (i for i in range(10))

def gen():
    for i in range(10):
        yield i
gen2 = gen()

print(gen1) # <generator object <genexpr> at 0x00000000>
print(gen2) # <generator object gen at 0x00000000>

例 1.3 中,两个生成器的作用完全相同,但还是有些区别,不知道怎么描述? 一个是生成器,一个是生成器表达式,但都属于生成器类型……感觉好别扭。

不管怎样,要理解 yield 语句,关键就是了解 python 的生成器。 用官网的说法12, 生成器就是一个返回迭代器(iterator)的函数。 和普通函数唯一的区别就是这个函数包含 yield 语句。

使用生成器,有两种方法。

# example 2.1

def gen():
    yield 1

# 2.1.1
g1 = gen()
for i in g1:
    print(i)

# 2.1.2
g2 = gen()
print( next(g2) )

像例 2.1 里的代码一样, 可以用for语句遍历生成器(注意是遍历生成器gen(),而不是函数gen), 也可以手动使用next函数获取生成器的值。 两者都是使用生成器的__next__方法来获取生成器的值的(?)。

下面再深入一点点。

# example 3.1

def gen():
    yield 1
    yield 2

generator = gen()

for value in generator:
    print(value)

虽然例 3.1 的生成器实在傻的可以,但是用来解释生成器工作还是可以的。

for 语句在碰到生成器 generator 的时候, 调用generator.__next__()获取生成器的返回值 (不知道底层是不是这样,总之这么理解吧)。__next__()每次调用,可以理解为执行了一次generator() 执行到 yield 的时候,生成器返回了 i 的值(本例中也就是 1)并停止。 这就像普通函数碰到 return 时一样,剩下的代码都被忽略了。 不同的地方在于,python 会记录这个停止的位置。 当再次执行generator()的时候,python 从这个停止位置开始执行而不是开头, 也就是说这次返回了 2。再执行generator(),已经没有 yield 语句了, 就抛出了 StopIteration 。这和其他迭代器是类似的。

要点就一个,python 会记录停止的位置,并且下次从这个位置继续执行。 可以去回顾下例 1.3,就是循环,暂停,再循环的过程。

知道这点,然后可以再深入一点。下面可以算是生成器的高级用法了吧。

# example 4.1

""" output "WHT!" """

def gen():
    while True:
        hello_world = yield
        print(hello_world)

g = gen()

next(g)

g.send("WTF!")
g.send("what the fuck!!")

第一次看到这种代码肯定不知道发生了什么……至少我不知道。 不过我之前就提示了一点,要理解 yield,就必须了解 python 的生成器。

生成器有这么几个方法

  • __next__ 不用说了,每次 for 还有 next 都是调用这个方法。
  • send(value) 用 value 对 yield 语句赋值,再执行接下来的代码直到下个 yield。
  • throw(type[, value[, traceback]]) 抛出错误,类似于 raise 吧。
  • close() 告诉生成器,你已经死了。再调用会抛出 StopIteration。

其实还有这么几个属性没列出来:

  • gi_code 不知道啥用,生成器的代码?
  • gi_frame 不知道啥用,环境变量?
  • gi_running 总算知道了,查看生成器是否再运行。

知道了上面这些,就可以理解例 4.1 的代码了。 其实就是用send方法对hello_world进行了赋值……

python 的代码还是很清晰的,但这里还是解释下那个 next 调用和 while。 在调用send方法前,必须先调用一次__next__,让生成器执行到 yield 语句处, 才能进行赋值。外面加上 while 循环是为了避免出现send之后, 生成器没有 yield 语句了,抛出 StopIteration 的情况。

其实我都没碰到过这么使用生成器的场景,似乎写协程时用的上,这里就先不提了。

另外,每次调用 next 还是挺麻烦的事情,这个工作可以交给修饰器。 代码见例 4.2。

# example 4.2

from functools import wraps

def coroutine(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        f = func(*args, **kwargs)
        next(f)
        return f
    return wrapper

目前就了解到这么多,就先写到这里了。研究过协程,再看看能否继续扩展吧。

总结一下:

  1. 使用 yield 语句的函数返回的是生成器。
  2. __next__获取生成器的值。
  3. 每次碰到 yield 语句会返回并记录当前位置。
  4. 可以对 yield 语句进行赋值。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值