Python的asyncio,期货和收益率

 mobiledu2502916313 发布于 2023-02-08 16:51

考虑以下程序(在CPython 3.4.0b1上运行):

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个(相关)问题:

    即使启动了装饰器fast_sqrt,Python似乎也fast_sqrt完全消除了创建的Future ,并float返回了plain .然后在炸毁run_test()yield from

    为什么我需要评估future.result()run_test检索火的值例外呢?该文件说,yield from "暂停协程,直到将来完成,然后返回未来的结果,或将引发异常".为什么我需要手动解决未来的结果?

这是我得到的:

oberstet@COREI7 ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)
$ python3 -V
Python 3.4.0b1
oberstet@COREI7 ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)
$ python3 test3.py

1.4142135623730951 
 exception: 'float' object has no attribute 'result'

Future 
 result: 1.4142135623730951
 exception: negative number

Future 
 exception: negative number
oberstet@COREI7 ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)

好的,我找到了"问题".将yield from asyncio.sleepslow_sqrt会使其自动协同程序.等待需要以不同的方式完成:

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个变种都在这里.

1 个回答
  • 关于#1:Python没有这样的事情.请注意,fast_sqrt您编写的函数(即在任何装饰器之前)不是生成器函数,协程函数,任务或您想要调用的任何函数.它是一个普通的函数同步运行并返回你在return语句后写的内容.根据存在的@coroutine不同,会发生很多不同的事情.运气不好导致同样的错误.

      没有装饰器,fast_sqrt(x)就像普通函数一样运行并返回float的未来(无论上下文如何).那个未来被消耗掉future = yield from ...,留下future一个浮动(没有result方法).

      使用装饰器,调用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
    

    (Modulo它不能用于fast_sqrt编写,并且不会获得额外的异步,因为未来已经从它返回时完成了coro_returning_a_future.)

    你的核心问题似乎是你混淆了协同程序和未来.你的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)
    

    2023-02-08 16:56 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有