假设我有一个用于执行 I/O 操作的类:
class CommunicationStack:
def __init__(self, socket):
self.socket = socket
def sync_get_data(self):
...
def sync_send_data(self):
...
async def async_get_data(self):
...
async def async_send_data(self):
...
正如您所看到的,它对于相同的操作有同步和异步变体,但手动编写 async_get_data
或 sync_get_data
会稍微不方便。我正在寻找一种聪明的方法来拥有相同的界面,例如
def get_data(self):
... # call sync variant or return an awaitable, depending on caller type
def send_data(self):
... # call sync variant or return an awaitable, depending on caller type
因此可以方便地使用,例如:
stack = CommunicationStack(...)
def thread_procedure():
data = stack.get_data() # get_data returns data
async def task_procedure():
data = await stack.get_data() # get_data returns an awaitable
我相信这可以通过检查或一些棘手的方式来完成black magic :
def is_caller_coroutine():
return sys._getframe(2).f_code.co_flags & 0x380
检查调用者是协程还是函数,但这似乎是一个糟糕的设计,会扰乱Python的内部结构。
<小时/>问题是:选择合适变体的好方法是什么?或者是否有更好的方法来设计一切,例如使用 adapters或者开发两个独立的 AsyncCommunicationStack
和 SyncCommunicationStack
类?
最佳答案
如果您想以与常规函数相同的方式调用异步函数,您可能有兴趣使用 gevent .
asyncio
希望您将函数显式标记为 async
并在发生异步情况的任何地方显式使用 await
。换句话说,asyncio
希望您为同步和异步代码拥有不同的接口(interface)。
这是intentional decision旨在解决并发问题,当隐藏代码的异步性质时(例如在 gevent 中),实现并发问题要困难得多。
所以是的 - 如果您想支持这两个世界,两个不同的独立 AsyncCommunicationStack
和 CommunicationStack
类是一种可行的方法。尽管一旦有了异步版本,您就可以用它编写关键代码并使其同步,只需使用 asyncio.run() 运行它即可。 。唯一的问题是,此后您将无法返回异步世界。
关于python - 如何在Python中选择异步或同步方法变体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56524743/