博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
生成器和协程 —— 你想知道的都在这里了
阅读量:4974 次
发布时间:2019-06-12

本文共 10953 字,大约阅读时间需要 36 分钟。

理解生成器

定义生成器 

yield关键字,可以让我们定义一个生成器函数。

def generator_func():    print('a')    yield 1g = generator_func()print(g) >>> 

推动生成器

使用next函数从生成器中取值

def generator_func():    print('a')    yield 1g = generator_func()ret1 = next(g)print(ret1) >>> a 1

 

使用next可以推动生成器的执行,下面的代码,我们可以看到每一次执行next可以让generator_func中的代码从上一个位置开始继续执行到yield,并且将yield后面的值返回到函数外部,最终我们可以执行到yield 3。

def generator_func():    print('a')    yield 1    print('b')    yield 2    print('c')    yield 3    print('d')g = generator_func()ret1 = next(g)print(ret1)ret2 = next(g)print(ret2)ret3 = next(g)print(ret3) >>>

  a

  1
  b
  2
  c
  3

当函数中已经没有更多的yield时继续执行next(g),遇到StopIteration

def generator_func():    print('a')    yield 1    print('b')    yield 2    print('c')    yield 3    print('d')g = generator_func()ret1 = next(g)print(ret1)ret2 = next(g)print(ret2)ret3 = next(g)print(ret3)next(g)
next和StopIteration

 

send向生成器中发送数据。send的作用相当于next,只是在驱动生成器继续执行的同时还可以向生成器中传递数据。

import numbersdef cal_sum():    sum_num = 0    while True:        num = yield        if isinstance(num,numbers.Integral):            sum_num += num            print('sum :',sum_num)        elif num is None:            breakg = cal_sum()g.send(None)   # 相当于next(g),预激活生成器g.send(31)g.send(25)g.send(17)g.send(8)>>>sum : 31sum : 56sum : 73sum : 81

  

生成器中的return和StopIteration

import numbersdef cal_sum():    sum_num = 0    while True:        num = yield        if isinstance(num,numbers.Integral):            sum_num += num            print('sum :',sum_num)        elif num is None:            break    return sum_numg = cal_sum()g.send(None)   # 相当于next(g),预激活生成器g.send(31)g.send(25)g.send(17)g.send(8)g.send(None)   # 停止生成器>>>sum : 31sum : 56sum : 73sum : 81Traceback (most recent call last):  File "/Users/jingliyang/PycharmProjects/python的进阶/manager.py", line 19, in 
g.send(None)StopIteration: 81
import numbersdef cal_sum():    sum_num = 0    while True:        num = yield        if isinstance(num,numbers.Integral):            sum_num += num            print('sum :',sum_num)        elif num is None:            break    return sum_numg = cal_sum()g.send(None)   # 相当于next(g),预激活生成器g.send(31)g.send(25)g.send(17)g.send(8)try:    g.send(None)   # 停止生成器except StopIteration as e:    print(e.value)
异常处理以及获取return的值

生成器的close和throw

使用throw向生成器中抛一个异常

def throw_test():    print('a')    yield 1    print('b')    yield 2g = throw_test()next(g)g.throw(Exception,'value error') >>> a Traceback (most recent call last): File "/Users/jingliyang/PycharmProjects/python的进阶/manager.py", line 32, in 
g.throw(ValueError,'value error') # throw和send、next相同,都是驱动生成器继续执行,只不过throw用来向生成器中抛一个异常 File "/Users/jingliyang/PycharmProjects/python的进阶/manager.py", line 26, in throw_test yield 1 ValueError: value error
def throw_test():    print('a')    try:        yield 1    except ValueError:        pass    print('b')    yield 2g = throw_test()next(g)ret = g.throw(ValueError,'value error')  # throw和send、next相同,都是驱动生成器继续执行,只不过throw用来向生成器中抛一个异常print(ret)>>>ab2
throw+异常处理

使用close关闭一个生成器

def throw_test():    print('a')    yield 1    print('b')    yield 2g = throw_test()ret1 = next(g)print(ret1)g.close()next(g)>>>a1Traceback (most recent call last):  File "/Users/jingliyang/PycharmProjects/python的进阶/manager.py", line 45, in 
next(g)StopIteration

yield from关键字

yield from关键字可以直接返回一个生成器

l = ['h','e','l']dic = {
'l':'v1','o':'v2'}s = 'eva'def yield_from_gen(): for i in l: yield i for j in dic: yield j for k in s: yield kfor item in yield_from_gen(): print(item,end='')>>>helloeval = ['h','e','l']dic = {
'l':'v1','o':'v2'}s = 'eva'def yield_from_gen(): yield from l yield from dic yield from sfor item in yield_from_gen(): print(item,end='')>>>helloeva
from itertools import chainl = ['h','e','l']dic = {
'l':'v1','o':'v2'}s = 'eva'def yield_from_gen(): yield from chain(l,dic,s)for item in yield_from_gen(): print(item,end='')
chain和yield from

利用yield from完成股票的计算,yield from能够完成一个委派生成器的作用,在子生成器和调用者之间建立起一个双向通道。

def son_gen():    avg_num = 0    sum_num = 0    count = 1    while True:        num = yield avg_num        if num:            sum_num += num            avg_num = sum_num/count            count += 1        else:break    return avg_numdef depute_gen(key):    while True:        ret = yield from son_gen()        print(key,ret)def main():    shares_list= {        'sogou':[6.4,6.5,6.6,6.2,6.1,6.6,6.7],        'alibaba':[181.72,184.58,183.54,180,88,169.88,178.21,189.32],        '美团':[59.7,52.6,47.2,55.4,60.7,66.1,74.0]    }    for key in shares_list:        g = depute_gen(key)        next(g)        for v in shares_list[key]:            g.send(v)        g.send(None)main()

协程

概念

  根据给出的定义,“ 是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序”。从技术的角度来说,“协程就是你可以暂停执行的函数”。如果你把它理解成“就像生成器一样”,那么你就想对了。

使用yield实现协程

#基于yield实现异步import timedef consumer():    '''任务1:接收数据,处理数据'''    while True:        x=yielddef producer():    '''任务2:生产数据'''    g=consumer()    next(g)    for i in range(10000000):        g.send(i)producer()

 

使用yield from实现的协程

import datetimeimport heapq    # 堆模块import typesimport timeclass Task:    def __init__(self, wait_until, coro):        self.coro = coro        self.waiting_until = wait_until    def __eq__(self, other):        return self.waiting_until == other.waiting_until    def __lt__(self, other):        return self.waiting_until < other.waiting_untilclass SleepingLoop:    def __init__(self, *coros):        self._new = coros        self._waiting = []    def run_until_complete(self):        for coro in self._new:            wait_for = coro.send(None)            heapq.heappush(self._waiting, Task(wait_for, coro))        while self._waiting:            now = datetime.datetime.now()            task = heapq.heappop(self._waiting)            if now < task.waiting_until:                delta = task.waiting_until - now                time.sleep(delta.total_seconds())                now = datetime.datetime.now()            try:                print('*'*50)                wait_until = task.coro.send(now)                print('-'*50)                heapq.heappush(self._waiting, Task(wait_until, task.coro))            except StopIteration:                pass def sleep(seconds):    now = datetime.datetime.now()    wait_until = now + datetime.timedelta(seconds=seconds)    print('before yield wait_until')    actual = yield wait_until   # 返回一个datetime数据类型的时间    print('after yield wait_until')    return actual - nowdef countdown(label, length, *, delay=0):    print(label, 'waiting', delay, 'seconds before starting countdown')    delta = yield from sleep(delay)    print(label, 'starting after waiting', delta)    while length:        print(label, 'T-minus', length)        waited = yield from sleep(1)        length -= 1    print(label, 'lift-off!')def main():    loop = SleepingLoop(countdown('A', 5), countdown('B', 3, delay=2),                        countdown('C', 4, delay=1))    start = datetime.datetime.now()    loop.run_until_complete()    print('Total elapsed time is', datetime.datetime.now() - start)if __name__ == '__main__':    main()

await和async关键字

使用 async function 可以定义一个 异步函数,在async关键字定义的函数中不能出现yield和yield from

# 例1async def download(url):   # 加入新的关键字 async ,可以将任何一个普通函数变成协程    return 'eva'ret = download('http://www.baidu.com/')print(ret)  # 
ret.send(None) # StopIteration: eva# 例2async def download(url): return 'eva'def run(coroutine): try: coroutine.send(None) except StopIteration as e: return e.valuecoro = download('http://www.baidu.com/')ret = run(coro)print(ret)

async关键字不能和yield一起使用,引入coroutine装饰器来装饰downloader生成器。

await 操作符后面必须跟一个awaitable对象(通常用于等待一个会有io操作的任务, 它只能在异步函数 async function 内部使用

# 例3import types@types.coroutine      # 将一个生成器变成一个awaitable的对象def downloader(url):    yield 'aaa'async def download_url(url):   # 协程    waitable = downloader(url)    print(waitable)   # 
生成器 html = await waitable return htmlcoro = download_url('http://www.baidu.com')print(coro) #
ret = coro.send(None)print(ret)

 

asyncio模块

asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

coroutine+yield from
import asyncio@asyncio.coroutinedef hello():    print("Hello world!")    # 异步调用asyncio.sleep(1):    r = yield from asyncio.sleep(1)    print("Hello again!")# 获取EventLoop:loop = asyncio.get_event_loop()# 执行coroutineloop.run_until_complete(hello())loop.close()
async+await
import asyncioasync def hello():    print("Hello world!")    # 异步调用asyncio.sleep(1):    r = await asyncio.sleep(1)    print("Hello again!")# 获取EventLoop:loop = asyncio.get_event_loop()# 执行coroutineloop.run_until_complete(hello())loop.close()

 

执行多个任务

import asyncioasync def hello():    print("Hello world!")    await asyncio.sleep(1)    print("Hello again!")    return 'done'loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait([hello(),hello()]))loop.close()

 

获取返回值

import asyncioasync def hello():    print("Hello world!")    await asyncio.sleep(1)    print("Hello again!")    return 'done'loop = asyncio.get_event_loop()task = loop.create_task(hello())loop.run_until_complete(task)ret = task.result()print(ret)

 

执行多个任务获取返回值

import asyncioasync def hello(i):    print("Hello world!")    await asyncio.sleep(i)    print("Hello again!")    return 'done',iloop = asyncio.get_event_loop()task1 = loop.create_task(hello(2))task2 = loop.create_task(hello(1))task_l = [task1,task2]tasks = asyncio.wait(task_l)loop.run_until_complete(tasks)for t in task_l:    print(t.result())

 

执行多个任务按照返回的顺序获取返回值

import asyncioasync def hello(i):    print("Hello world!")    await asyncio.sleep(i)    print("Hello again!")    return 'done',iasync def main():    tasks = []    for i in range(20):        tasks.append(asyncio.ensure_future(hello((20-i)/10)))    for res in asyncio.as_completed(tasks):        result = await res        print(result)loop = asyncio.get_event_loop()loop.run_until_complete(main())

 

asyncio使用协程完成http访问

import asyncioasync def get_url():    reader,writer = await asyncio.open_connection('www.baidu.com',80)    writer.write(b'GET / HTTP/1.1\r\nHOST:www.baidu.com\r\nConnection:close\r\n\r\n')    all_lines = []    async for line in reader:        data = line.decode()        all_lines.append(data)    html = '\n'.join(all_lines)    return htmlasync def main():    tasks = []    for url in range(20):        tasks.append(asyncio.ensure_future(get_url()))    for res in asyncio.as_completed(tasks):        result = await res        print(result)if __name__ == '__main__':    loop = asyncio.get_event_loop()    loop.run_until_complete(main())  # 处理一个任务    loop.run_until_complete(asyncio.wait([main()]))  # 处理多个任务    task = loop.create_task(main())  # 使用create_task获取返回值    loop.run_until_complete(task)    loop.run_until_complete(asyncio.wait([task]))

 

gevent模块实现协程

 

转载于:https://www.cnblogs.com/Eva-J/p/10437164.html

你可能感兴趣的文章
[django实践]投票app
查看>>
[django]form的content-type(mime)
查看>>
JQUERY —— 绑定事件
查看>>
在TabControl中的TabPage选项卡中添加Form窗体
查看>>
oracle中SET DEFINE意思
查看>>
个人作业-最长英语链
查看>>
JMeter-性能测试之报表设定的注意事项
查看>>
1066-堆排序
查看>>
仿面包旅行个人中心下拉顶部背景放大高斯模糊效果
查看>>
强大的css3
查看>>
c#中的组件拖拽和MouseMove事件
查看>>
C# 小叙 Encoding (二)
查看>>
python创建对象数组避免浅拷贝
查看>>
CSS自学笔记(14):CSS3动画效果
查看>>
项目应用1
查看>>
Ubuntu下配置jdk和tomcat
查看>>
大型网站的演变升级
查看>>
图片延迟加载的实现
查看>>
C# 委托链(多播委托)
查看>>
解密个推持续集成
查看>>