https://zhuanlan.zhihu.com/p/27258289
https://docs.python.org/zh-cn/3/library/asyncio-task.html#running-an-asyncio-program
import asyncio
@asyncio.coroutine
def hello():
print("Hello world!")
# 异步调用asyncio.sleep(1):
r = yield from asyncio.sleep(1)
print("Hello again!")
# 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
loop.run_until_complete(hello())
loop.close()
@asyncio.coroutine把一个generator标记为coroutine类型,然后,我们就把这个coroutine扔到EventLoop中执行。
hello()会首先打印出Hello world!,然后,yield from语法可以让我们方便地调用另一个generator。由于asyncio.sleep()
也是一个coroutine,所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,
线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。
把asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,
而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行。
可迭代、迭代器、生成器
可迭代的对象,很好理解,我们很熟悉的:字符串,list,dict,tuple,deque等
import collections
from collections.abc import Iterable, Iterator, Generator
# 字符串
astr = "XiaoMing"
print("字符串:{}".format(astr))
print(isinstance(astr, Iterable))
print(isinstance(astr, Iterator))
print(isinstance(astr, Generator))
字符串:XiaoMing
True
False
False
这些可迭代对象都不是迭代器,也不是生成器。它们有一个共同点,就是它们都可以使用for来循环
可迭代对象,是其内部实现了,iter 这个魔术方法。
可以通过,dir()方法来查看是否有iter来判断一个变量是否是可迭代的。
迭代器其实就只是多了一个函数而已。就是next(),我们可以不再使用for循环来间断获取元素值。而可以直接使用next()方法来实现。
from collections.abc import Iterator
aStr = 'abcd' # 创建字符串,它是可迭代对象
aIterator = iter(aStr) # 通过iter(),将可迭代对象转换为一个迭代器
print(isinstance(aIterator, Iterator)) # True
next(aIterator) # a
next(aIterator) # b
next(aIterator) # c
next(aIterator) # d
迭代器,是其内部实现了,next 这个魔术方法。(Python3.x)
可以通过,dir()方法来查看是否有next来判断一个变量是否是迭代器的。
迭代器,是在可迭代的基础上,加了一个next()方法。
而生成器,则是在迭代器的基础上(可以用for循环,可以使用next()),再实现了yield
yield 是什么东西呢,它相当于我们函数里的return。在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。正是由于这个机制,才使用生成器在Python编程中大放异彩。实现节省内存,实现异步编程。
如何运行/激活生成器
激活主要有两个方法
- 使用next()
- 使用generator.send(None)
def mygen(n):
now = 0
while now < n:
yield now
now += 1
if __name__ == '__main__':
gen = mygen(4)
# 通过交替执行,来说明这两种方法是等价的。
print(gen.send(None))
print(next(gen))
print(gen.send(None))
print(next(gen))
生成器的执行状态
- GEN_CREATED # 等待开始执行
- GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
- GEN_SUSPENDED # 在yield表达式处暂停
- GEN_CLOSED # 执行结束
yield from关键字
yield from 将一个可迭代对象变成一个迭代器返回,也可以说,yield from关键字可以直接返回一个生成器
>>> # yield from 将一个可迭代对象变成一个迭代器返回
>>> def func2():
lst = ['str', 'tuple', 'list', 'dict', 'set']
yield from lst
>>> gen2 = func2()
>>> next(gen2)
'str'
>>> next(gen2)
'tuple'
>>> for i in gen2:
print(i)
从生成器过渡到协程:yield
我们知道生成器为我们引入了暂停函数执行(yield)的功能。当有了暂停的功能之后,人们就想能不能在生成器暂停的时候向其发送一点东西(其实上面也有提及:send(None))
协程通过使用 yield 暂停生成器,可以将程序的执行流程交给其他的子程序,从而实现不同子程序的之间的交替执行
In [142]: def test():
...: i=0
...: while i<5:
...: t = yield i
...: print(t,i)
...: i+=1
In [148]: a=test()
In [149]: a.send(None)
Out[149]: 0
In [150]: a.send("34")
34 0
Out[150]: 1
* yield i 是将i return给外部调用程序。
* t = yield 可以接收外部程序通过send()发送的信息,并赋值给t
为什么python的yield第一次不能用send发送数据?
https://www.zhihu.com/question/28105502
因为最开始,gen()函数运行到yield i时,是从gen()把i的值抛回给调用者所以最开始send进去一个具体值,是几个意思?顺序不对啊!!现在人家并不是回到gen()生成器,而是第一次运行gen()函数时人家要从gen()那里出来啊send(var)是发生在回到gen()时的操作,而不是从gen()出来时的操作,这是需要明确差别的一点
因为协程是一个线程执行,那怎么利用多核CPU呢?
最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
Python对协程的支持是通过generator实现的。
asyncio模块
https://www.cnblogs.com/51try-again/p/11074621.html
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,
然后在coroutine内部用yield from调用另一个coroutine实现异步操作。
asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。
coroutine+yield from
import asyncio
@asyncio.coroutine
def hello():
print("Nice to learn asyncio.coroutine!")
# 异步调用asyncio.sleep(1):
r = yield from asyncio.sleep(1)
print("Nice to learn asyncio.coroutine again !")
# 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
loop.run_until_complete(hello())
loop.close()
Nice to learn asyncio.coroutine !
Nice to learn asyncio.coroutine again !
为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。
请注意,async和 await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换:
- 把@asyncio.coroutine替换为async;
- 把yield from替换为await。
执行多个任务
import threading
import asyncio
async def hello():
print('Hello Python! (%s)' % threading.currentThread())
await asyncio.sleep(1)
print('Hello Python again! (%s)' % threading.currentThread())
loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
Hello Python! (<_MainThread(MainThread, started 4536)>)
Hello Python! (<_MainThread(MainThread, started 4536)>)
Hello Python again! (<_MainThread(MainThread, started 4536)>)
Hello Python again! (<_MainThread(MainThread, started 4536)>)
执行多个任务获取返回值
import threading
import asyncio
async def hello(seq):
print('Hello Python! (%s)' % threading.currentThread())
await asyncio.sleep(1)
print('Hello Python again! (%s)' % threading.currentThread())
return "It's done", seq
loop = asyncio.get_event_loop()
task1 = loop.create_task(hello(2))
task2 = loop.create_task(hello(1))
task_list = [task1, task2]
tasks = asyncio.wait(task_list)
loop.run_until_complete(tasks)
for t in task_list:
print(t.result())
可等待 对象有三种主要类型: 协程, 任务 和 Future.
-
协程函数: 定义形式为
async def
的函数; -
协程对象: 调用 协程函数 所返回的对象。
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
网友评论