Python协程的理解

1. Python中yield的理解

def call(k):
    return k*3
def yield_test1(n):
    for i in range(n):
        z = yield call(i)
        print("z=%s:j=%s" % (z,i))

n = yield_test1(10)
print(next(n))
print(n.send(11))

执行结果

0
z=11:j=0
3

Process finished with exit code 0


* 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator(生成器)。
* next(n)相当于send(None),每一执行next或send时,程序遇到yield的时候暂停,并将yield 同行的语句返回(有点类似return)。
* 第一次执行next(n)执行到yield call(i),返回结果0。第二次send(11)将执行,print(“z=%s:j=%s” % (z,i)), yield call(i),返回结果打印z=11:j=0,返回值3。
* send(11)中send的值将会被z = yield call(i) 中的z接收,所以打印出z=11
* yield执行到最后抛出StopIteration错误表示无法继续返回下一个值了,本例中for语句自带捕获StopIteration异常,终止循环,其他的情况可能会需要进行异常处理。
* generator(生成器)一般没有return语句,如果任然要写return “123”语句,但是无法正常获得这个值,需要按照以下方法:

def fib(max):
  n, a, b = 0, 0, 1
  while n < max:
    yield b
    a, b = b, a + b
    n = n + 1
  return 'done'
# 捕获Generator的返回值
g = fib(6)
while True:
  try:
    x=next(g)
    print('g=',x)
  except StopIteration as e:
    print('Generrator return value:', e.value)
    break

执行获取结果:

g= 1
g= 1
g= 2
g= 3
g= 5
g= 8
Generrator return value: done

2. Python中yield from的理解

#输出斐波那契數列前 N 个数
def fab3(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        # print b
        a, b = b, a + b
        n = n + 1

def f_wrapper1(f):
    for g  in f:
        yield g
wrap = f_wrapper1(fab3(5))
for i in wrap:
    print(i,end=' ')

输出结果:

1 1 2 3 5 
Process finished with exit code 0
使用yield from修改(使用yield from代替for循环)
#输出斐波那契數列前 N 个数
def fab3(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        # print b
        a, b = b, a + b
        n = n + 1

def f_wrapper2(f):
  yield from f  # 注意此处必须是一个可生成对象
wrap = f_wrapper2(fab3(5))
for i in wrap:
  print(i, end=' ')
yield from iterable本质上等于for item in iterable: yield item的缩写版
print('yield from包含多个子程序')
def g(x):
    yield from range(x, 0, -1)
    print('kk')
    yield from range(x)
print(list(g(5)))
for g  in g(6):
    print(g,end=',')

输出结果:

yield from包含多个子程序
kk
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
6,5,4,3,2,1,kk
0,1,2,3,4,5,
Process finished with exit code 0
利用yield from语句向生成器(协程)传送数据
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,换回生产者继续生产,效率极高:
import time
def consumer_work(len):
  # 读取send传进的数据,并模拟进行处理数据
  print("writer:")
  w = ''
  while True:
    w = yield w  # w接收send传进的数据,同时也是返回的数据
    print('[CONSUMER] Consuming %s...>> ', w)
    w *= len  # 将返回的数据乘以100
    time.sleep(0.1)


def consumer(coro):
  yield from coro  # 将数据传递到协程(生成器)对象中


def produce(c):
  c.send(None)  # "prime" the coroutine
  for i in range(5):
    print('[Produce] Producing %s----', i)
    w = c.send(i)  # 发送完成后进入协程中执行
    print('[Produce] receive %s----', w)
  c.close()


c1 = consumer_work(100)
produce(consumer(c1))

执行结果:

writer:
[Produce] Producing %s---- 0
[CONSUMER] Consuming %s...>>  0
[Produce] receive %s---- 0
[Produce] Producing %s---- 1
[CONSUMER] Consuming %s...>>  1
[Produce] receive %s---- 100
[Produce] Producing %s---- 2
[CONSUMER] Consuming %s...>>  2
[Produce] receive %s---- 200
[Produce] Producing %s---- 3
[CONSUMER] Consuming %s...>>  3
[Produce] receive %s---- 300
[Produce] Producing %s---- 4
[CONSUMER] Consuming %s...>>  4
[Produce] receive %s---- 400

Process finished with exit code 0

3. Python中@asyncio.coroutine的理解

asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。
用asyncio实现Hello world代码如下:
import asyncio

@asyncio.coroutine
def work(flg, n):
    yield from asyncio.sleep(n)
    print("word is %s" % flg)



@asyncio.coroutine
def hello(flg, n):
    print("Hello world! %s" % flg)
    # 异步调用asyncio.sleep(1):
    r = yield from work(flg, n)
    print("Hello again! %s" % flg)

# 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
tasks = [hello("zhangsan", 10), hello("lisi", 7)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
@asyncio.coroutine把一个generator标记为coroutine类型,然后,我们就把这个coroutine扔到EventLoop中执行。
上例子中tasks变量中有两个coroutine类型,所以在执行到第一个的asyncio.sleep(n)的时候,不回去等待执行完成,而是开始执行第二个coroutine类型,因此可以实现并发执行。
为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。
请注意,async和await是针对coroutine的新语法,要使用新的语法,只需要做两步替换:
  1. 把@asyncio.coroutine替换为async;
  2. 把yield from替换为await。

4. Python中asyncio和aiohttp的理解

asyncio可以实现单线程并发IO操作。如果仅用在客户端,发挥的威力不大。如果把asyncio用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine实现多用户的高并发支持。
asyncio实现了TCP、UDP、SSL等协议,aiohttp则是基于asyncio实现的HTTP框架。
import asyncio

from aiohttp import web

async def index(request):
    await asyncio.sleep(0.5)
    return web.Response(body=b'<h1>Index</h1>',content_type='text/html')

async def hello(request):
    await asyncio.sleep(0.5)
    text = '<h1>hello, %s!</h1>' % request.match_info['name']
    return web.Response(body=text.encode('utf-8'),content_type='text/html')

async def init(loop):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', index)
    app.router.add_route('GET', '/hello/{name}', hello)
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
    print('Server started at http://127.0.0.1:8000...')
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()
参考资料
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值