當前位置:編程學習大全網 - 編程語言 - python異步有哪些方式

python異步有哪些方式

yield相當於return,他將相應的值返回給調用next()或者send()的調用者,從而交出了CPU使用權,而當調用者再次調用next()或者send()的時候,又會返回到yield中斷的地方,如果send有參數,還會將參數返回給yield賦值的變量,如果沒有就和next()壹樣賦值為None。但是這裏會遇到壹個問題,就是嵌套使用generator時外層的generator需要寫大量代碼,看如下示例:?

註意以下代碼均在Python3.6上運行調試

#!/usr/bin/env python# encoding:utf-8def inner_generator():

i = 0

while True:

i = yield i if i > 10: raise StopIterationdef outer_generator():

print("do something before yield")

from_inner = 0

from_outer = 1

g = inner_generator()

g.send(None) while 1: try:

from_inner = g.send(from_outer)

from_outer = yield from_inner except StopIteration: breakdef main():

g = outer_generator()

g.send(None)

i = 0

while 1: try:

i = g.send(i + 1)

print(i) except StopIteration: breakif __name__ == '__main__':

main()1234567891011121314151617181920212223242526272829303132333435363738394041

為了簡化,在Python3.3中引入了yield from

yield from

使用yield from有兩個好處,

1、可以將main中send的參數壹直返回給最裏層的generator,?

2、同時我們也不需要再使用while循環和send (), next()來進行叠代。

我們可以將上邊的代碼修改如下:

def inner_generator():

i = 0

while True:

i = yield i if i > 10: raise StopIterationdef outer_generator():

print("do something before coroutine start") yield from inner_generator()def main():

g = outer_generator()

g.send(None)

i = 0

while 1: try:

i = g.send(i + 1)

print(i) except StopIteration: breakif __name__ == '__main__':

main()1234567891011121314151617181920212223242526

執行結果如下:

do something before coroutine start123456789101234567891011

這裏inner_generator()中執行的代碼片段我們實際就可以認為是協程,所以總的來說邏輯圖如下:?

接下來我們就看下究竟協程是啥樣子

協程coroutine

協程的概念應該是從進程和線程演變而來的,他們都是獨立的執行壹段代碼,但是不同是線程比進程要輕量級,協程比線程還要輕量級。多線程在同壹個進程中執行,而協程通常也是在壹個線程當中執行。它們的關系圖如下:

我們都知道Python由於GIL(Global Interpreter Lock)原因,其線程效率並不高,並且在*nix系統中,創建線程的開銷並不比進程小,因此在並發操作時,多線程的效率還是受到了很大制約的。所以後來人們發現通過yield來中斷代碼片段的執行,同時交出了cpu的使用權,於是協程的概念產生了。在Python3.4正式引入了協程的概念,代碼示例如下:

import asyncio# Borrowed from plete(asyncio.wait(tasks))

loop.close()12345678910111213141516

示例顯示了在Python3.4引入兩個重要概念協程和事件循環,?

通過修飾符@asyncio.coroutine定義了壹個協程,而通過event loop來執行tasks中所有的協程任務。之後在Python3.5引入了新的async & await語法,從而有了原生協程的概念。

async & await

在Python3.5中,引入了aync&await 語法結構,通過”aync def”可以定義壹個協程代碼片段,作用類似於Python3.4中的@asyncio.coroutine修飾符,而await則相當於”yield from”。

先來看壹段代碼,這個是我剛開始使用async&await語法時,寫的壹段小程序。

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time

async def wait_download(url):

response = await requets.get(url)

print("get {} response complete.".format(url))

async def main():

start = time.time()

await asyncio.wait([

wait_download(""),

wait_download(""),

wait_download("")])

end = time.time()

print("Complete in {} seconds".format(end - start))

loop = asyncio.get_event_loop()

loop.run_until_complete(main())12345678910111213141516171819202122232425

這裏會收到這樣的報錯:

Task exception was never retrieved

future: <Task finished coro=<wait_download() done, defined at asynctest.py:9> exception=TypeError("object Response can't be used in 'await' expression",)>

Traceback (most recent call last):

File "asynctest.py", line 10, in wait_download

data = await requests.get(url)

TypeError: object Response can't be used in 'await' expression123456

這是由於requests.get()函數返回的Response對象不能用於await表達式,可是如果不能用於await,還怎麽樣來實現異步呢??

原來Python的await表達式是類似於”yield from”的東西,但是await會去做參數檢查,它要求await表達式中的對象必須是awaitable的,那啥是awaitable呢? awaitable對象必須滿足如下條件中其中之壹:

1、A native coroutine object returned from a native coroutine function .

原生協程對象

2、A generator-based coroutine object returned from a function decorated with types.coroutine() .

types.coroutine()修飾的基於生成器的協程對象,註意不是Python3.4中asyncio.coroutine

3、An object with an await method returning an iterator.

實現了await method,並在其中返回了iterator的對象

根據這些條件定義,我們可以修改代碼如下:

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time

async def download(url): # 通過async def定義的函數是原生的協程對象

response = requests.get(url)

print(response.text)

async def wait_download(url):

await download(url) # 這裏download(url)就是壹個原生的協程對象

print("get {} data complete.".format(url))

async def main():

start = time.time()

await asyncio.wait([

wait_download(""),

wait_download(""),

wait_download("")])

end = time.time()

print("Complete in {} seconds".format(end - start))

loop = asyncio.get_event_loop()

loop.run_until_complete(main())123456789101112131415161718192021222324252627282930

好了現在壹個真正的實現了異步編程的小程序終於誕生了。?

而目前更牛逼的異步是使用uvloop或者pyuv,這兩個最新的Python庫都是libuv實現的,可以提供更加高效的event loop。

uvloop和pyuv

pyuv實現了Python2.x和3.x,但是該項目在github上已經許久沒有更新了,不知道是否還有人在維護。?

uvloop只實現了3.x, 但是該項目在github上始終活躍。

它們的使用也非常簡單,以uvloop為例,只需要添加以下代碼就可以了

import asyncioimport uvloop

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())123

  • 上一篇:校園成長類故事
  • 下一篇:長城坦克編程
  • copyright 2024編程學習大全網