您的位置:首页 > 编程语言 > Python开发

python-yield详解

2017-04-17 00:49 363 查看
yield详解
1、包含yield的函数
假如你看到某个函数包含了yield,这意味着这个函数已经是一个Generator,它的执行会和其他普通的函数有很多不同。比如下面的简单的函数:
可以看到,调用h()之后,print 语句并没有执行!这就是yield,那么,如何让print 语句执行呢?这就是后面要讨论的问题,通过后面的讨论和学习,就会明白yield的工作原理了。
In [52]: def h():
....:         print 'To be brave'
....:         yield 5
....:
In [53]: h()
Out[53]: <generator object h at 0x2a6f370>
In [54]: def h():
print 'To be brave'
In [55]:
In [55]: h()
To be brave
2、yield是一个表达式
m = yield 5
表达式(yield 5)的返回值将赋值给m,所以,认为 m = 5 是错误的。那么如何获取(yield 5)的返回值呢?需要用到后面要介绍的send(msg)方法。
4、send(msg) 与 next()
其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做
c.next() 和 c.send(None) 作用是一样的。
In [78]: def h():
print 'Wen Chuan',
m = yield 5  # Fighting!
print'm is %s:' % m
d = yield 12
print 'We are together!'
....:
In [79]: c=h()
In [80]: c.next()
Wen ChuanOut[80]: 5
In [81]: c.next()
m is None:
Out[81]: 12
In [82]: c=h()
In [83]: c.next()     #相当于c.send(None)
Wen ChuanOut[83]: 5
In [84]: c.send('fighting')    #(yield 5)表达式被赋予了'Fighting!'
m is fighting:
Out[84]: 12
需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。

5. send(msg) 与 next()的返回值
send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是下一个yield表达式的参数。比如yield 5,则返回 5 。到这里,是不是明白了一些什么东西?本文第一个例子中,通过for i in alist 遍历 Generator,其实是每次都调用了alist.Next(),而每次alist.Next()的返回值正是yield的参数,即我们开始认为被压进去的东东。我们再延续上面的例子:
In [91]: def h():
print 'Wen Chuan',
m = yield 5  # Fighting!
print m
d = yield 12
print 'We are together!'
....:
In [92]: c=h()
In [93]: m=c.next()   #m 获取了yield 5 的参数值 5
Wen Chuan
In [94]: d=c.send('fighting')   #d 获取了yield 12 的参数值12
fighting  --- 表达式(yield 12)的值m为fighting
In [96]: print 'we will never forget the date',m,'.',d
we will never forget the date 5 . 12

《提高你的Python: 解释‘yield’和‘Generators(生成器)’》
https://www.oschina.net/translate/improve-your-python-yield-and-generators-explained
yield就是专门给生成器用的return(加上点小魔法)。
下面是一个简单的生成器函数:
In [3]: def simple_generator_function():
...:     yield 1
...:     yield 2
...:     yield 3
...:
这里有两个简单的方法来使用它:
In [4]: for value in simple_generator_function():
...:     print(value)
...:
1
2
3
In [5]: our_generator = simple_generator_function()
In [6]: next(our_generator)
Out[6]: 1
In [7]: next(our_generator)
Out[7]: 2
In [8]: next(our_generator)
Out[8]: 3

In [1]: def is_prime(number):
...:     if number > 1:
...:         if number == 2:
...:             return True
...:         if number % 2 == 0:
...:             return False
...:         for current in range(3, int(math.sqrt(number) + 1), 2):
...:             if number % current == 0:
...:                 return False
...:         return True
...:     return False
...:
In [2]: def get_primes(number):
...:     while True:
...:         if is_prime(number):
...:             yield number
...:         number += 1
...:

yield执行流程分析
调用get_primes
In [30]: def solve_number_10():
....:     # She *is* working on Project Euler #10, I knew it!
....:     total = 2
....:     for next_prime in get_primes(3):
....:         if next_prime < 2000000:
....:             total += next_prime
....:         else:
....:             print(total)
....:             return
....:
我们来看一下solve_number_10的for循环中对get_primes的调用,观察一下前几个元素是如何创建的有助于我们的理解。当for循环从get_primes请求第一个值时,我们进入get_primes,这时与进入普通函数没有区别。
进入第三行的while循环

停在if条件判断(3是素数)

通过yield将3和执行控制权返回给solve_number_10

接下来,回到insolve_number_10:
for循环得到返回值3

for循环将其赋给next_prime

total加上next_prime

for循环从get_primes请求下一个值

这次,进入get_primes时并没有从开头执行,我们从第5行继续执行,也就是上次离开的地方。
In [2]: def get_primes(number):
...:     while True:
...:         if is_prime(number):
...:             yield number
...:         number += 1     # <<<<<<<<<<
...:
最关键的是,number还保持我们上次调用yield时的值(例如3)。记住,yield会将值传给next()的调用方,同时还会保存生成器函数的“状态”。接下来,number加到4,回到while循环的开始处,然后继续增加直到得到下一个素数(5)。我们再一次把number的值通过yield返回给solve_number_10的for循环。这个周期会一直执行,直到for循环结束(得到的素数大于2,000,000)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  工作原理 yield 如何