Python学习笔记----协程

# 协程

  • - 参考资料
  • - http://python.jobbole.com/86481/
  • - http://python.jobbole.com/87310/
  • - https://segmentfault.com/a/1190000009781688


# 迭代器
- 可迭代(Iterable):直接作用于for循环的变量
- 迭代器(Iterator):不但可以作用于for循环,还可以被next调用
- list是典型的可迭代对象,但不是迭代器
- 通过isinstance判断
- iterable和iterator可以转换
-通过iter函数


# 可迭代(test.py) --->test(1)
####1
# 可迭代
l = [i for i in range(10)]


# l是可迭代的,但不是迭代器
for idx in l:
print(idx)
# range是个迭代器
for i in range(5):
print(i)


#判断是否可迭代
from collections import Iterable
l1 = [1,2,3,4,5]


print(isinstance(l1, Iterable))


from collections import Iterator
print(isinstance(l1, Iterator))
# isinstance案例 --->test(2)
####2
#iter函数
from collections import Iterable
from collections import Iterator
s = 'I love feifei'
print(isinstance(s, Iterable))
print(isinstance(s, Iterator))


s_iter = iter(s)
print(isinstance(s_iter, Iterable))
print(isinstance(s_iter, Iterator))
# 判断某个变量是否是一个实例




#生成器
-generator:一边循环一边计算下一个元素的机制\算法
-需要满足三个条件:
-每次调用都生产出for循环需要的下一个元素
-如果达到最后一个后,爆出StopIterration异常
-可以被next函数调用


-如何生成一个生成器 ---->test(3)
-直接使用
-如果函数中包含yield,则这个函数就叫生成器
-next调用函数,遇到yield返回
l = [x*x for x in range(5)] #放在中括号中是列表生成器
g = (x*x for x in range(5)) #放在小括号中就是生成器


print(type(l))
print(type(g))






######4
#函数案例
#def odd():
#    print("Step 1")
#    print("Step 2")
#    print("Step 3")
#    return None


#生成器案例
#在函数odd中,yield负责返回
#def odd():
#    print("Step 1")
#    yield 1
#    print("Step 2")
#    yield 2
#    print("Step 3")
#    yield 3


##odd() 是调用生成器
#g = odd()
#one = next(g)
#print(one)


#two = next(g)
#print(two)


#three = next(g)
#print(three)




#for 循环调用生成器
#def fib(max):
#    n,a,b = 0, 0, 1
#    while n<max:
#        print(b)
#        a,b = b, a+b
#        n += 1
#    return 'Done'


#fib(5)


# 斐波那契数列的生成器写法
def fib(max):
n,a,b = 0, 0, 1
while n<max:
print(b)
a,b = b, a+b
n += 1
return 'Done'


g = fib(10)


#for i in range(6):
#    rst = next(g)
#    print(rst)


'''
生成器的典型用法是在for中使用
比较常用的典型生成器就是range
'''
for i in g:
print(i)




#协程
-历史历程
-3.4引入协程,用yield实现
-3.5引入协程语法
-实现的协程比较好的包邮asyncio, tornado, gevent
定义:协程是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序
从技术角度讲,协程就是一个你可以暂停执行的函数,或者干脆把协程理解成生成器
-协程的实现:
-yield返回
-send调用


-协程的四个状态
-inspect.getgeneratorstate(...)函数确定,该函数会返回下述字符创中的一个:
-GEN_CREATED:等待开始执行
-GEN_RUNNING:解释器正在执行
-GEN_SUSPENED:yield表达式处暂停
-GEN_CLOSED:执行结束
-next预激(prime)
-代码案例2
协程代码案例1
def simple_coroutine():
print('->start')
x = yield 
print('->recived', x)
#主线程
sc = simple_coroutine()
print(1111)
#可以使用sc.send(none),效果一样
next(sc) #预激


print(22222)
sc.send('feifei')




案例代码2, 协程的状态
def simple_coroutine(a):
print('->start')


b = yield a 
print('->recived', a, b)


c = yield a+b 
print('->recived', a, b, c)


sc = simple_coroutine(5)


aa = next(sc)
print(aa)
bb = sc.send(6) #5,6
print(bb)
cc = sc.send(7) #5,6,7
print(cc)
-协程终止:
-协程中未处理的异常会向上冒泡,传给next函数或send方法的调用方(即触发协程的对象)
-终止程序的一种方式:发送某个哨符值,让协程退出,内置的None和Ellipsis等常量经常用作哨符值==。


- yield from
-调用协程为了得到返回值,协程必须正常终止
-生成器正常终止会发出StopIteration异常,异常对象的value属性保存返回值
-yield from从内部捕获StopIteration异常
-案例3
案例3
def gen():
for c in "AB":
yield c


#list直接使用生成器作为参数
print(list(gen()))


def gen_new():
yield from "AB"


print(list(gen_new()))
-委派生成器
-包含yield from表达式的生成器函数
-委派生成器在yield from表达式处暂停,调用方可以直接把数据发给子生成器
-子生成器在最后,解释器会抛出StopIteration,并且把返回值附加到异常对象上
-案例4
#案例4 委派生成器
#from collections import namedtuple


#'''
#解释:
#1. 外层 for 循环每次迭代会新建一个 grouper 实例,赋值给 coroutine 变量; grouper 是委派生成器。
#2. 调用 next(coroutine),预激委派生成器 grouper,此时进入 while True 循环,调用子生成器 averager 后,在 yield from 表达式处暂停。
#3. 内层 for 循环调用 coroutine.send(value),直接把值传给子生成器 averager。同时,当前的 grouper 实例(coroutine)在 yield from 表达式处暂停。
#4. 内层循环结束后, grouper 实例依旧在 yield from 表达式处暂停,因此, grouper函数定义体中为 results[key] 赋值的语句还没有执行。
#5. coroutine.send(None) 终止 averager 子生成器,子生成器抛出 StopIteration 异常并将返回的数据包含在异常对象的value中,yield from 可以直接抓取 StopItration 异常并将异常对象的 value 赋值给 results[key]
#'''
#ResClass = namedtuple('Res', 'count average')




## 子生成器
#def averager():
#    total = 0.0
#    count = 0
#    average = None


#    while True:
#        term = yield
#        # None是哨兵值
#        if term is None:
#            break
#        total += term
#        count += 1
#        average = total / count


#    return ResClass(count, average)




## 委派生成器
#def grouper(storages, key):
#    while True:
#        # 获取averager()返回的值
#        storages[key] = yield from averager()




## 客户端代码
#def client():
#    process_data = {
#        'boys_2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
#        'boys_1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46]
#    }


#    storages = {}
#    for k, v in process_data.items():
#        # 获得协程
#        coroutine = grouper(storages, k)


#        # 预激协程
#        next(coroutine)


#        # 发送数据到协程
#        for dt in v:
#            coroutine.send(dt)


#        # 终止协程
#        coroutine.send(None)
#    print(storages)


## run
#client()


#asyncio
- Python3.4开始引入标准库当中,内置对一部io的支持
- asyncio本身是一个消息循环
- 步骤:
- 创建消息循环
- 把协程导入
- 关闭
-案例1+案例2
###案例1
import threading
# 引入异步io包
import asyncio


# 使用协程装饰器
@asyncio.coroutine
def hello():
print('Hello world! (%s)'% threading.currentThread())
print('Start........(%s)'% threading.currentThread())
yield from asyncio.sleep(10)
print('Done.......(%s)'% threading.currentThread())
print('Hello again! (%s)' % threading.currentThread())


# 启动消息循环
loop = asyncio.get_event_loop()
# 定义任务
tasks = [hello(), hello()]
# asyncio使用wait等待tasks执行完毕
loop.run_until_complete(asyncio.wait(tasks))
# 关闭消息循环
loop.close()






##案例2
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()
# http协议的换行使用\r\n
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
# Ignore the body, close the socket
writer.close()


loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()


# async and await
- 为了更好的表示异步io
- Python3.5引入
- 使用上,可以简单的进行替换
- 用async替换@asyncio.coroutine
- await 替换 yield from
-案例3
##案例3
import threading
引入异步io包
import asyncio


使用协程装饰器
async def hello():
print('Hello world! (%s)'% threading.currentThread())
print('Start........(%s)'% threading.currentThread())
await asyncio.sleep(10)
print('Done.......(%s)'% threading.currentThread())
print('Hello again! (%s)' % threading.currentThread())


启动消息循环
loop = asyncio.get_event_loop()
定义任务
tasks = [hello(), hello()]
asyncio使用wait等待tasks执行完毕
loop.run_until_complete(asyncio.wait(tasks))
关闭消息循环
loop.close()


# aiohttp
- asyncio实现单线程的并发io,在客户端用处不大
- 在服务器端可以asyncio+coroutine配合,因为http是io操作
- asyncio实现了tcp, udp, ssl等协议
- aiohttp是给予asyncio实现的http框架
- pip install aiohttp安装


#aiohttp案例
import asyncio


from aiohttp import web


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


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'))


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()


# concurrent.futures
- Python3新增的库
- 类似其他语言的线程池的概念
- 利用multiprocessing实现真正的并行计算
- 核心原理: 以子进程的形式,并行运行多个Python解释器,从而令Python程序可以利用多核CPU来提升执行速度。由于子进程与主解释器相分离,所以它们的全局解释器锁也是相互独立的。每个子进程都能够完整的使用一个CPU内核。
- concurrent.futures.Executor
-ThreadPoolExecutor
-ProcessPoolExecutor
-submit(fn, args, kwargs)
-fn:异步执行的函数
-args,kwargs:参数
#案例1
# 案例1
from concurrent.futures import ThreadPoolExecutor
import time




def return_future(msg):
    time.sleep(3)
    return msg




# 创建一个线程池
pool = ThreadPoolExecutor(max_workers=2)


# 往线程池加入2个task
f1 = pool.submit(return_future, 'hello')
f2 = pool.submit(return_future, 'world')


# 等待执行完毕
print(f1.done())
time.sleep(3)
print(f2.done())


# 结果
print(f1.result())
print(f2.result())




# current中的map函数
- map(fn, \*iterables, timeout=None)
- 跟map函数类似
- 函数需要异步执行
- timeout:超时时间
- map跟submit使用一个就行
#案例2
#案例2
import time,re
import os,datetime
from concurrent import futures


data = ['1','2']


def wait_on(argument):
   print(argument)
   time.sleep(2)
   return "ok"


ex = futures.ThreadPoolExecutor(max_workers=2)
for i in ex.map(wait_on,data):
   print(i)
# future
- 未来需要完成的任务
- future实例有Excutor.submit创建
- 案例 3
#案例3
from concurrent.futures import ThreadPoolExecutor as Pool
from concurrent.futures import as_completed
import requests


URLS = ['http://qq.com', 'http://sina.com', 'http://www.baidu.com', ]




def task(url, timeout=10):
return requests.get(url, timeout=timeout)




with Pool(max_workers=3) as executor:
future_tasks = [executor.submit(task, url) for url in URLS]


for f in future_tasks:
if f.running():
print('%s is running' % str(f))


for f in as_completed(future_tasks):
try:
ret = f.done()
if ret:
f_ret = f.result()
print('%s, done, result: %s, %s' % (str(f), f_ret.url, len(f_ret.content)))
except Exception as e:
f.cancel()
print(str(e))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值