1  yield基本用法

典型的例子

  斐波那契(Fibonacci)數列是一个非常简单的递归数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到。1 2 3 5 8……

def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
# print b
a, b = b, a + b
n = n + 1

  yield 的作用就是把一个函数变成一个generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个生成器,如调用fab函数, 不会执行该函数,而是返回一个iterable迭代对象!

  在for循环执行时,每次循环都会相当于执行生成器的next函数,才开始执行fab函数的内部代码,执行到yield b时,fab函数就返回一个迭代值,然后挂起。

  下次迭代时,代码从yield b的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到yield。

更多yield例子:

#!/usr/bin/python
def a():
print ("do a() will not print out")
yield 5
a()
print ("===============test a()") def b():
print ("list generator will in def , print here...")
yield 5
g_obj = b()
print ("===============g_obj test b: %s" % g_obj)
print ("just generator obj, not in b def")
print ("list_g: %s" % list(g_obj)) def c():
print ("next() will here... test generator next(), next attrbute not in python3, python2.6 is exist")
yield 5
print ("test generator next2")
g_obj = c()
print ("===============g_obj test c: %s" % g_obj)
#g_obj.next()
#print ("dir g_obj: %s " % dir(g_obj)) def d():
global m
global n
print ("send() will here... test generator send()")
m = yield 5
print ("send input is m : %s" % m)
n = yield 6
print ("test generator send2") g_obj = d()
print ("===============g_obj test d: %s" % g_obj)
s_return1 = g_obj.send(None)
s_return2 = g_obj.send("send twice")
print ("the next send input will be the result of last yield, just like m is : %s, s_return1 is : %s, s_return2 is : %s" % (m, s_return1, s_return2))
print ("not next send so n is undefind, n is : %s" % n)

运行结果:

===============test a()
===============g_obj test b: <generator object b at 0x7f740b7fc750>
just generator obj, not in b def
list generator will in def , print here...
list_g: [5]
===============g_obj test c: <generator object c at 0x7f740b7fc7e0>
===============g_obj test d: <generator object d at 0x7f740b7fc750>
send() will here... test generator send()
send input is m : send twice
the next send input will be the result of last yield, just like m is : send twice, s_return1 is : 5, s_return2 is : 6
Traceback (most recent call last):
File "./yield0.py", line 40, in <module>
print ("not next send so n is undefind, n is : %s" % n)
NameError: name 'n' is not defined

send用法说明:

关于输入:send的输入是本次遇到yield时,先赋值给yield表达式的结果。有点难懂,详细说明。

1、如m = yield 5,这个表达式,是分两次yield完成的,第一次执行后一半,即返回5,下次send时,才执行前一半,即把后一次send的输入赋值给m。

2、所以,第一次使用send,输入必须是None,开启生成器,因为本次遇到yield后,yield返回后,就完结了,并没有一个执行到赋值给m的过程。m的初值是第二个send()输入参数。

3、最后一个最后一个send,把输入给了上一次yield表达式,所以最后一个n=yield 6,语句执行后,n是未定义的。

关于输出:比较简单,就是yield的结果。如s_return = send(None),由于yield 5,所以s_return= 5

2  使用yield实现协程

举例:生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产。

#!/usr/bin/python

def consumer():
r = ''
while True:
n = yield r
if not n:
print("not n...")
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK' def produce(c):
f = c.send(None)
print('[PRODUCER] Consumer first return: %s' % f)
n = 0
while n < 2:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close() c = consumer()
produce(c)

运行结果:

[PRODUCER] Consumer first return:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK

协程的说明:

注意到consumer函数是一个generator,把一个consumer传入produce后:

  1. 首先调用c.send(None)启动生成器;
  2. 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
  3. consumer通过yield拿到消息,处理,又通过yield把结果传回;
  4. produce拿到consumer处理的结果,继续生产下一条消息;
  5. produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

3  yield from基本用法

从python3.3新增语法yield from,在python3.4中asyncio的微线程的实现依赖此语法。

先从generator中套generator的需求入手。

举例:生成器调用子生成器,父生成器输入什么,调用完子生成器后,同样返回什么。

def i_yield_whatever_input_is():
input = 0
while True:
print("1: before gi yield input=%s" % input)
input = yield input
print("2: after gi yield input=%s" % input) def wrap_generator1():
for i in i_yield_whatever_input_is():
print("3: before g1 yield i=%s" % i)
yield i g = wrap_generator1()
print("4: after send None return: %s" % g.send(None))
print("4: after send 1 return: %s" % g.send(1))
print("4: after send 2 return: %s" % g.send(2))

未到达预期的运行结果:

1: before gi yield input=0
3: before g1 yield i=0
4: after send None return: 0
2: after gi yield input=None
1: before gi yield input=None
3: before g1 yield i=None
4: after send 1 return: None
2: after gi yield input=None
1: before gi yield input=None
3: before g1 yield i=None
4: after send 2 return: None

  显然不是预期“输入什么,返回什么”。由于send的输入到wrap_generator后,无法输入给子生成器,因此,子生成器i_yield_whatever_input_is的输入是None,只能yield None。

  使用yield from,可以将send的输入,传递给子生成器,父生成器代码修改如下:

def wrap_generator2():
yield from i_yield_whatever_input_is() g = wrap_generator2()
print("4: after send None return: %s" % g.send(None))
print("4: after send 1 return: %s" % g.send(1))
print("4: after send 2 return: %s" % g.send(2))

达到预期的运行结果:

1: before gi yield input=0
4: after send None return: 0
2: after gi yield input=1
1: before gi yield input=1
4: after send 1 return: 1
2: after gi yield input=2
1: before gi yield input=2
4: after send 2 return: 2

4 使用yield from实现asyncio

简单的例子(两个函数并发执行,函数内部的sleep不互相阻塞其它函数):

#!/usr/bin/python
import asyncio
import threading @asyncio.coroutine
def hello():
print("2.1 befor yield from asyncio sleep")
r = yield from asyncio.sleep(2)
print("2.1 after yield from asyncio sleep") def hello2():
print("2.2 befor yield from asyncio sleep")
r = yield from asyncio.sleep(5)
print("2.2 after yield from asyncio sleep") loop = asyncio.get_event_loop()
print ("1. after get event loop") #loop.run_until_complete(hello()) tasks = [hello2(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
print ("2. after run") loop.close()
print ("3. after close")

运行结果:

1. after get event loop
2.2 befor yield from asyncio sleep
2.1 befor yield from asyncio sleep
===等待两个函数sleep返回===
2.1 after yield from asyncio sleep
2.2 after yield from asyncio sleep
2. after run
3. after close

复杂的例子(并发同时访问多个WEB服务器):

#!/usr/bin/python
import asyncio @asyncio.coroutine
def wget(host):
print("wget %s..." % host)
connect = asyncio.open_connection(host, 80)
reader, writer = yield from connect
header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8')) #向服务器发送请求
yield from writer.drain()
while True:
line = yield from reader.readline() #读取服务器返回的数据
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
writer.close() loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.baidu.com', 'www.sina.com','www.taobao.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

运行结果:并发访问,服务器一旦返回,立即打印到屏幕。

wget www.baidu.com...
wget www.taobao.com...
wget www.sina.com...
www.baidu.com header > HTTP/1.1 200 OK
www.baidu.com header > Date: Fri, 12 Jun 2015 03:17:20 GMT
www.baidu.com header > Content-Type: text/html
www.baidu.com header > Content-Length: 14613
www.baidu.com header > Last-Modified: Wed, 03 Sep 2014 02:48:32 GMT
www.baidu.com header > Connection: Close
www.baidu.com header > Vary: Accept-Encoding
www.baidu.com header > Set-Cookie: BAIDUID=052DF57419E7322485FE496F7CFD60DF:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
www.baidu.com header > Set-Cookie: BIDUPSID=052DF57419E7322485FE496F7CFD60DF; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
www.baidu.com header > Set-Cookie: PSTM=1434079040; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
www.baidu.com header > Set-Cookie: BDSVRTM=0; path=/
www.baidu.com header > P3P: CP=" OTI DSP COR IVA OUR IND COM "
www.baidu.com header > Server: BWS/1.1
www.baidu.com header > X-UA-Compatible: IE=Edge,chrome=1
www.baidu.com header > Pragma: no-cache
www.baidu.com header > Cache-control: no-cache
www.baidu.com header > BDPAGETYPE: 1
www.baidu.com header > BDQID: 0x82714a2100005cd6
www.baidu.com header > BDUSERID: 0
www.baidu.com header > Accept-Ranges: bytes
www.sina.com header > HTTP/1.1 301 Moved Permanently
www.sina.com header > Server: nginx
www.sina.com header > Date: Fri, 12 Jun 2015 03:15:36 GMT
www.sina.com header > Content-Type: text/html
www.sina.com header > Location: http://www.sina.com.cn/
www.sina.com header > Expires: Fri, 12 Jun 2015 03:17:36 GMT
www.sina.com header > Cache-Control: max-age=120
www.sina.com header > Age: 104
www.sina.com header > Content-Length: 178
www.sina.com header > X-Cache: HIT from ja180-186.sina.com.cn
www.sina.com header > Connection: close
www.taobao.com header > HTTP/1.1 200 OK
www.taobao.com header > Server: Tengine
www.taobao.com header > Date: Fri, 12 Jun 2015 03:17:20 GMT
www.taobao.com header > Content-Type: text/html; charset=gbk
www.taobao.com header > Connection: close
www.taobao.com header > Vary: Accept-Encoding
www.taobao.com header > Set-Cookie: CAT=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0
www.taobao.com header > Set-Cookie: thw=cn; Path=/; Domain=.taobao.com; Expires=Sat, 11-Jun-16 03:17:20 GMT;

python yield用法举例说明的更多相关文章

  1. Python yield用法浅析(stackoverflow)

    这是stackoverflow上一个关于python中yield用法的帖子,这里翻译自投票最高的一个回答,原文链接 here 问题 Python中yield关键字的用途是什么?它有什么作用?例如,我试 ...

  2. python yield用法 (tornado, coroutine)

    yield关键字用来定义生成器(Generator),其具体功能是可以当return使用,从函数里返回一个值,不同之处是用yield返回之后,可以让函数从上回yield返回的地点继续执行.也就是说,y ...

  3. Python yield 用法

    一.环境 python 3.6 二.yield 说明 yield 是一个生成器,可以用于迭代.也是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面(右边)的值. 重点是: ...

  4. Python yield用法

    yield 官方称是一种生成器,每每遇到这样包含这个关键字的代码,往往有些难读.def testyield(count): for x in xrange(count): print "te ...

  5. python with用法举例

    我们知道在操作文件对象的时候可以这么写 with open('a.txt') as f: '代码块' 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明_ ...

  6. Python yield 的基本概念和用法

    之前解析MQTT协议时,需要做一个等分字节流的操作,其中用到了yield关键字,如下: def get_var_length(hstring): m = 1 v = 0 for element in ...

  7. python中yield用法

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...

  8. 【转】Python yield 使用浅析

    转载地址: www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/ Python yield 使用浅析 初学 Python 的开发者经 ...

  9. Python yield 使用浅析(转)

    Python yield 使用浅析 初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关键字,然而,带有 yield 的函数执行流程却和普通函数不一样,yield 到 ...

随机推荐

  1. Python补充01 序列的方法

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在快速教程中,我们了解了最基本的序列(sequence).回忆一下,序列包含有定值 ...

  2. div滚动到页面顶端后固定住

    <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 配置IISExpress允许外部访问

    1.找到IISExpress的配置文件,位于 <文档>/IISExpress/config文件夹下,打开applicationhost.config,找到如下代码: <site na ...

  4. 虚拟机与CentOS的安装设置。

    点击下一步: 点击稍后安装操作系统. 选择Linux系统,继续下一步 为了方便以后查找文件我们在这把虚拟机的名称改成自己喜欢的, 位置也是在大点的磁盘重新建一个专门存放的文件夹,做到规范. 在这推荐N ...

  5. [HDU 1011] Starship Troopers (树形dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 dp[u][i]为以u为根节点的,花了不超过i元钱能够得到的最大价值 因为题目里说要访问子节点必 ...

  6. java使用jacob将office转pdf

    1.此处代码是把office文档转换成pdf的工具类,在BS架构的产品中,我们可以使用基于JS的pdf插件显示pdf文档,但是前提IE需要按照adobe的pdf软件,对于非IE不用安装.2.可以基于f ...

  7. MFC学习 画图设置字体按钮风格

    修改按钮样式时, 设置按钮关联哪个按钮类, 按钮类是自己写的, 从CButton继承, 重写DrawItem可修改按钮样式. 代码中包括画线, 点, 圆, 设置这些的样式, 如线粗, 颜色, 字体. ...

  8. 修改 sql server 2008R2的端口,配置防火墙允许远程访问SQL Server 2008 R2

    1.先修改 sql server 2008R2的端口号吧,1433经常成为别人入侵的端口,在sql server 配置管理器 -->sql server 网络配置-->MSSQLSERVE ...

  9. 文件的输出与载入之java操作

    一.前言 学习java没多久,关键是没怎么系统学过.都是看别人的代码来学习的.今天就把一直以来让我头痛的java  IO 的一些基本操作来记录下来,加深记忆. 二.java导入文件到内存中 首先放一个 ...

  10. rabbitmq学习笔记

    1 基本概念 rabbitmq server(broker server):rabbitmq服务 client:包括producers和consumer message:包括payload和label ...