2013-12-22 83 views
11

考慮下面的程序(在CPython的運行3.4.0b1):Python的ASYNCIO,期貨收益率從

import math 
import asyncio 
from asyncio import coroutine 

@coroutine 
def fast_sqrt(x): 
    future = asyncio.Future() 
    if x >= 0: 
     future.set_result(math.sqrt(x)) 
    else: 
     future.set_exception(Exception("negative number")) 
    return future 


def slow_sqrt(x): 
    yield from asyncio.sleep(1) 
    future = asyncio.Future() 
    if x >= 0: 
     future.set_result(math.sqrt(x)) 
    else: 
     future.set_exception(Exception("negative number")) 
    return future 


@coroutine 
def run_test(): 
    for x in [2, -2]: 
     for f in [fast_sqrt, slow_sqrt]: 
     try: 
      future = yield from f(x) 
      print("\n{} {}".format(future, type(future))) 
      res = future.result() 
      print("{} result: {}".format(f, res)) 
     except Exception as e: 
      print("{} exception: {}".format(f, e)) 


loop = asyncio.get_event_loop() 
loop.run_until_complete(run_test()) 

我有2個(相關的)問題:

  1. 即使在裝飾上fast_sqrt,Python似乎會優化掉fast_sqrt中創建的Future,並返回一個普通的float。然後在run_test()yield from

  2. 炸燬爲什麼我需要評估在run_testfuture.result()檢索火的值例外呢? docsyield from <future>「暫停協程,直到未來完成,然後返回未來的結果,或引發異常」。爲什麼我需要手動調整未來的結果?

這裏是我所得到的:

[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master) 
$ python3 -V 
Python 3.4.0b1 
[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master) 
$ python3 test3.py 

1.4142135623730951 <class 'float'> 
<function fast_sqrt at 0x00B889C0> exception: 'float' object has no attribute 'result' 

Future<result=1.4142135623730951> <class 'asyncio.futures.Future'> 
<function slow_sqrt at 0x02AC8810> result: 1.4142135623730951 
<function fast_sqrt at 0x00B889C0> exception: negative number 

Future<exception=Exception('negative number',)> <class 'asyncio.futures.Future'> 
<function slow_sqrt at 0x02AC8810> exception: negative number 
[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master) 

好吧,我發現了 「問題」。在slow_sqrt中的yield from asyncio.sleep將自動使其成爲協程。等待需要以不同的方式完成:

def slow_sqrt(x): 
    loop = asyncio.get_event_loop() 
    future = asyncio.Future() 
    def doit(): 
     if x >= 0: 
     future.set_result(math.sqrt(x)) 
     else: 
     future.set_exception(Exception("negative number")) 
    loop.call_later(1, doit) 
    return future 

所有4個變體都是here

+0

這是在Python3.3還是隻是在測試版中的錯誤? – Mark

+0

我沒有3.3安裝..會檢查。 – oberstet

+0

關於第一個問題,*哪裏*爆炸了?在'未來=產量從f(x)'或'res = future.result()'? – delnan

回答

6

關於#1:Python沒有這樣的事情。請注意,您編寫的fast_sqrt函數(即在任何裝飾器之前)不是生成器函數,協程函數,任務或任何您想調用的函數。這是一個同步運行的普通函數,返回return語句後寫入的內容。根據@coroutine的存在,會發生非常不同的事情。只是運氣不好,都會導致相同的錯誤。

  1. 沒有裝飾,fast_sqrt(x)運行像普通函數是和返回float的未來(不管上下文的)。 future = yield from ...將消耗該未來,並將future保留爲浮點型(其沒有result方法)。

  2. 隨着裝飾,呼叫f(x)經過由@coroutine創建一個包裝函數。這個包裝函數調用fast_sqrt,並使用yield from <future>構造爲您解包生成的未來。因此,這個包裝函數本身就是一個協程。因此,future = yield from ...協同程序和葉future再次浮動。

關於#2,yield from <future>確實工作(如上所述,您使用的是未經修飾的fast_sqrt在使用它),你也可以這樣寫:

future = yield from coro_returning_a_future(x) 
res = yield from future 

(模,它對於fast_sqrt不起作用,因爲寫入時不會產生額外的異步性,因爲將來從coro_returning_a_future返回時已經完成。)

您的核心問題se ems是你混淆了協程和期貨。您的sqrt實現都嘗試成爲導致期貨的異步任務。 從我有限的經驗來看,這不是通常寫asyncio代碼的方式。它允許您將未來的構建和將來代表的計算分解爲兩個獨立的異步任務。但是你不這樣做(你返回一個已經完成的未來)。在大多數情況下,這不是一個有用的概念:如果你必須異步做一些計算,你可以將它作爲協同程序(可以掛起)寫入另一個線程並與之通信使用yield from <future>。不是都。

爲了平方根計算異步,只寫一個普通協程做了計算和return結果(coroutine裝飾會變成fast_sqrt成異步運行任務,可以在被等待)。

@coroutine 
def fast_sqrt(x): 
    if x >= 0: 
     return math.sqrt(x) 
    else: 
     raise Exception("negative number") 

@coroutine # for documentation, not strictly necessary 
def slow_sqrt(x): 
    yield from asyncio.sleep(1) 
    if x >= 0: 
     return math.sqrt(x) 
    else: 
     raise Exception("negative number") 

... 
res = yield from f(x) 
assert isinstance(res, float) 
+0

我知道如何處理協程和沒有期貨(https://github.com/oberstet/scratchbox/blob/master /python/asyncio/test2.py),但是這個_requires_函數要被修飾。我正在尋找期貨和無裝飾者(非侵入性的rgd API)的解決方案。 – oberstet

+0

@oberstet您可以編寫一個返回未來的普通非協程函數。您可以編寫一個普通函數來返回普通值並同步使用它們。你可以在沒有裝飾器的情況下編寫協程(雖然你顯然仍然需要遵循關於'yield from'等的asyncio約定)。我不知道什麼是「rgd API」,但如果你想更詳細地解決你的問題,你可以打開另一個問題。 – delnan

+0

我的「問題」是'slow_srqt'內的'asyncio.sleep'產生的結果使它自動成爲協程。我正在尋找這4個變種:https://github.com/oberstet/scratchbox/blob/master/python/asyncio/test3.py – oberstet