我正在开发一个库,它应该支持同步和异步用户,同时最大限度地减少库中的代码重复.理想的情况是实现库异步(因为库进行远程API调用),并通过包装器添加对同步的支持.请考虑将以下函数作为库的一部分.

# Library code

async def internal_function():
  """Internal part of my library doing HTTP call"""
  pass

我的 idea 是提供一个包装器来检测库的用户是否使用异步.

# Library code

def api_call():
   """Public api of my library"""
    if asyncio.get_event_loop().is_running():
        # Async caller
        return internal_function()  # This returns a coroutine, so the caller needs to await it
    
    # Sync caller, so we need to run the coroutine in a blocking way
    return asyncio.run(internal_function())

起初,这似乎是解决之道.有了这个,我就可以支持

  • 从异步函数调用该函数的用户,
  • 从笔记本(也是异步)调用该函数的用户,以及
  • 从纯Python脚本调用该函数的用户(这里它回退到阻塞asyncio.run).

但是,在某些情况下,函数是从事件循环内调用的,但直接调用方是同步函数.

# Code from users of the library

async def entrypoint():
    """This entrypoint is called in an event loop, e.g. within fastlib"""
    return legacy_sync_code()  # Call to sync function

def legacy_sync_code():
    """Legacy code that does not support async"""
    # Call to library code from sync function,
    # expect value not coroutine, could not use await
    api_response = api_call()
    return api_response.json()  # Needs to be value, not coroutine

在最后一行中,json()的调用失败.api_call()错误地推断调用方可以等待响应,并返回协程.

为了支持这类用户,我需要

  • 识别直接调用函数是否是同步且期望值而不是路由,
  • 有办法以阻塞的方式等待internal_function()的结果.使用asyncio.run只能在普通的Python脚本中工作,如果代码是从堆栈跟踪中更高层的事件循环中调用的,则会失败.

如果我的库提供两个函数(例如,api_call_async()api_call_sync()),则可以减轻第一点.

我希望我的观点足够清楚.我不明白为什么这从根本上来说是不可能的,然而,如果Python的设计不允许我以完全透明的方式支持同步和异步用户,我可以接受.

推荐答案

不要试图过于灵活或解决尚未发生的问题.这将使您的工作变得更加困难,并且可能使您的代码更难开发和维护,结果并不像您想象的那样灵活.不照看其他开发人员并让他们承担一些责任并没有什么错.如果他们想要同步或异步,让他们自己 Select .还要注意的是,看起来对您有好处的东西(在实现细节等方面,比如检测同步/异步)可能会 destruct 其他人的交易,因为您可能正在做一些最初没有被要求的事情.很容易为了没有真正的好处而智胜自己,然后浪费大量的时间来解决你刚刚愉快地制造的问题.我个人会 Select 单独的api_call_sync()api_call_async()方法,但也要确保我的库文档非常清楚,并讨论您现在认为可能存在问题的所有边缘 case ,这样我的用户就可以全面了解战场,尽他们所能拍摄最好的照片.

Python相关问答推荐

在Python中对分层父/子列表进行排序

大小为M的第N位_计数(或人口计数)的公式

如何请求使用Python将文件下载到带有登录名的门户网站?

运输问题分支定界法&

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

将JSON对象转换为Dataframe

将pandas导出到CSV数据,但在此之前,将日期按最小到最大排序

Python中的变量每次增加超过1

使用BeautifulSoup抓取所有链接

Flask Jinja2如果语句总是计算为false&

为什么'if x is None:pass'比'x is None'单独使用更快?

从源代码显示不同的输出(机器学习)(Python)

当HTTP 201响应包含 Big Data 的POST请求时,应该是什么?  

使用np.fft.fft2和cv2.dft重现相位谱.为什么结果并不相似呢?

Pandas:将值从一列移动到适当的列

如何将列表从a迭代到z-以抓取数据并将其转换为DataFrame?

#将多条一维曲线计算成其二维数组(图像)表示

如何在networkx图中提取和绘制直接邻居(以及邻居的邻居)?

Sknowled线性回归()不需要迭代和学习率作为参数

Fake pathlib.使用pyfakefs的类变量中的路径'