1、关于概念定义的收集和整理
迭代器
- 可迭代(Iterable): 直接作用与for循环的变量
- 迭代器(Iterator):不但可以作用于for循环,还可以被next调用
- list是典型的可迭代对象,但不是迭代器
生成器
- generator: 一边循环一遍计算下一个元素的机制/算法
- 满足三个条件:
- 每次调用都能生产出for循环需要的下一个元素
- 如果达到最后一个,会提示StopIteration异常
- 可以被next函数调用
- 如何生成一个生成器
- 直接使用
- 如果函数中包含yield,则这个函数就叫生成器
- next调用函数,遇到yield返回
协程
- 历史历程
- 3.4引入协程,用yield实现
- 3.5引入协程语法
- 实现的协程比较好的包有asyncio, tornado, gevent
- 从技术角度讲,协程就是一个你可以暂停执行的函数
- 协程的实现:
- yield 返回
- next|send 调用
- 协程的四个状态
- 协程终止
- 协程中未处理的异常会向上冒泡,传给next函数或者send方法的调用方(即触发协程的对象)
- 终止协程的一种方式,发送某个哨符值,让协程退出,内置的None和Ellipsis等常量经常用作哨符值
- yield from
- 调用协程为了得到返回值,协程必须正常终止
- 生成器正常终止会发出StopIteration异常,异常对象的value属性保存返回值
- yield from从内部捕获StopIteration异常
- 相当于中间的第三方通道
- 委派生成器
- 包含yield from表达式的生成器函数
- 委派生成器在yield from表达式出暂停,调用方可以直接把数据发给子生成器
- 子生成器再把产出的值发给调用方
- 子生成器在最后,解释器会抛出StopIteraton,并且把返回值附加到异常对象上
2、函数和方法的收集和整理
asyncio
- Python3.4开始引入标准库当中,内置对异步IO的支持
- asyncio本身是一个消息循环
- 步骤:
- 创建消息循环
- 把协程导入
- 关闭
async , await
- 为了更好的表示异步IO
- python3.5引入
- 使用上进行简单替换
- 用async替换@asyncio.coroutine
- 用await替换 yield from
aiohttp
- asyncio实现单线程的并发IO ,在客户端用处不大
- 在服务器端可以用asyncio + coroutine配合,因为HTTP是IO操作
- asyncio实现了TCP,UDP,SSL等协议
- aiohttp是给予asyncio实现的http框架
- 安装:pip install aiohttp
concurrent.futures
- python3新增的库
- 类似其他语言的线程池的概念
- 利用mulipprocessing实现真正的并行计算
- 核心原理:
- 以子进程的形式,并行多个python解释器,从而令python程序可以利用多核CPU来提升执行速度
- 由于子进程与主解释器相分离,所以他们的全局解释器锁也是相互独立的
- 每个子进程都能够完整的使用一个CPU核心
- concurrent.futures.Executor
- ThreadPoolExecutor
- ProcessPoolExecutor
- 执行的时候需要自行选择
- submit(func, args, kwargs)
- func: 异步执行的函数
- *args,**args[]
concurrent中map函数
- map(func, *iterables, timeout=None)
- 跟map函数类似
- 函数需要异步执行
3、实例的收集和归纳
- 实例01——asyncio
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等待task执行完毕
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
- 实例02——
import asyncio
async def wget(host):
print("wget %s..." % host)
# 异步请求网址
connect = asyncio.open_connection(host, 80)
# 注意yield from的用法
reader, writer = await connect
header = "GET / Http/1.0\r\nHost: %s\r\n\r\n" % host
writer.write(header.encode("utf-8"))
await writer.drain()
while True:
line = await reader.readline()
# HTTP协议的换行使用\r\n
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.sina.com.cn", "www.163.com", "www.taobao.com"]]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
- 实例03——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()
02 和 03代码在jupyter notebook运行都是失败的,但在pycharm下运行是正常的
- 实例04——concurrent.futures
import time
from concurrent.futures import ThreadPoolExecutor
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, "MImi")
# 自带done()方法,等待执行完毕
print(f1.done())
time.sleep(2)
print(f2.done())
print(f1.result())
print(f2.result())
- 实例05——委派生成器
from collections import namedtuple
#
ResClass = namedtuple("Res", "count average")
# 子生成器
def averager():
total = 0.0
count = 0
average = None
while True:
# term相当一个哨符值,当调用send传入的值为None时,终止协程
term = yield
if term is None:
break
# 子生成器需要完成的计算功能
total += term
count += 1
average = total / count
return ResClass(count, average)
# 委派生成器
def grouper(storages, key):
while True:
# 将client获取averager()返回的值
storages[key] = yield from averager()
# 客户端代码
def client():
# 定义两组预处理的数据,以字典形式
process_data = {
'b2': [45.5, 89.7, 45.7, 82.4, 34.4, 56, 9],
'b1': [1.34, 3.45, 6.12, 4.12, 9.01, 0.54]
}
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()
4、总结以及疑惑?心得?
协程是一个相对新的功能模块,也是作为可替代多线程threading的一个选择。
最后课题:由此引发一个联想,关于生产者消费者模型的流程,是否可以加入协程或者以之代替
代码生成中…
网上有一个Tkinter的贪吃蛇案例,其中就是包含了多线程和队列的内容