Python 根据实例化的是异步实例还是同步实例,返回包装

Python 根据实例化的是异步实例还是同步实例,返回包装,python,python-3.x,async-await,wrapper,python-asyncio,Python,Python 3.x,Async Await,Wrapper,Python Asyncio,有没有一种方法可以从基类中提取给定方法实例化的子类 我知道这个问题有点复杂,所以举个例子: from functools import wraps def my_wrapper(fn_to_wrap): @wraps(fn_to_wrap) async def async_wrapper(*args, **kwargs): await do_some_async_stuff() print('Did some async stuff')

有没有一种方法可以从基类中提取给定方法实例化的子类

我知道这个问题有点复杂,所以举个例子:

from functools import wraps

def my_wrapper(fn_to_wrap):

    @wraps(fn_to_wrap)
    async def async_wrapper(*args, **kwargs):
        await do_some_async_stuff()
        print('Did some async stuff')
        return fn_to_wrap(*args, **kwargs)

    @wraps(fn_to_wrap)
    def sync_wrapper(*args, **kwargs):
        do some_sync_stuff()
        print('Did some sync stuff')
        return fn_to_wrap(*args, **kwargs)

    # <my_problem>
    if fn_to_wrap belongs_to(SyncClass):
        return sync_wrapper
    else:
        return async_wrapper
    # </my_problem>


class BaseClass:
    @my_wrapper
    def fn_to_wrap(self):
        return 'Finally a return a value'

class SyncClass(BaseClass):
    def fn_to_call(self):
        return self.fn_to_wrap()


class AsyncClass(BaseClass):
    async def fn_to_call(self):
        return await self.fn_to_wrap()

那么,您将如何实施
来实现这些结果呢

[编辑]


我知道存在
inspect.iscoroutinefunction
inspect.iscoroutine
。但是这些都没有帮助,因为wrapped方法始终是同步的,而wrapper是异步任务的执行者。

如果允许
my\u wrapper
了解
AsyncClass
SyncClass
(或者您可以控制它们,并可以添加一个类属性,如
\u is\u sync
,告诉包装器正在处理哪种类型的类),您只需检查
self

无法从
位置执行此操作,因为那里还没有
self
;代码必须为同步和异步情况返回单个包装器。调用后,包装器必须检测异步情况,并在需要异步行为时返回实例化的
async def
。(返回协同路由对象的同步函数在功能上等同于协同路由函数,这与以
返回某个\u生成器()结尾的普通函数非常相似。
完全可用作生成器。)

下面是一个使用
isinstance
检测调用哪个变量的示例:

def my_wrapper(fn_to_wrap):
    async def async_wrapper(*args, **kwargs):
        await asyncio.sleep(.1)
        print('Did some async stuff')
        return fn_to_wrap(*args, **kwargs)

    @wraps(fn_to_wrap)
    def uni_wrapper(self, *args, **kwargs):
        # or if self._is_async, etc.
        if isinstance(self, AsyncClass):
            return async_wrapper(self, *args, **kwargs)
        time.sleep(.1)
        print('Did some sync stuff')
        return fn_to_wrap(self, *args, **kwargs)

    return uni_wrapper
该实现会产生所需的输出:

>>> x = SyncClass()
>>> x.fn_to_call()
Did some sync stuff
'Finally a return a value'
>>> async def test():
...     x = AsyncClass()
...     return await x.fn_to_call()
... 
>>> asyncio.get_event_loop().run_until_complete(test())
Did some async stuff
'Finally a return a value'
如果包装器无法区分
SyncClass
AsyncClass
,则上述解决方案将不起作用。有两个约束可能会阻止它这样做:

  • isinstance
    如果子类的数量是开放的,那么它将不起作用
  • 如果包装器作者不控制最终类,则自定义类属性将不起作用
在这种情况下,剩下的选项是求助于黑魔法来确定函数是从协同程序调用还是从同步函数调用。黑魔法由David Beazley在以下内容中方便地提供:

使用来自协同程序的
my\u wrapper
uni\u wrapper
部分如下所示:

    @wraps(fn_to_wrap)
    def uni_wrapper(*args, **kwargs):
        if from_coroutine():
            return async_wrapper(*args, **kwargs)
        time.sleep(.1)
        print('Did some sync stuff')
        return fn_to_wrap(*args, **kwargs)
…提供相同的结果


当然,您必须知道,在下一个Python版本中,黑魔法可能会在没有任何警告的情况下停止工作。但是,如果您知道自己在做什么,它可能会非常有用。

太棒了!谢谢:)
>>> x = SyncClass()
>>> x.fn_to_call()
Did some sync stuff
'Finally a return a value'
>>> async def test():
...     x = AsyncClass()
...     return await x.fn_to_call()
... 
>>> asyncio.get_event_loop().run_until_complete(test())
Did some async stuff
'Finally a return a value'
def from_coroutine():
    return sys._getframe(2).f_code.co_flags & 0x380
    @wraps(fn_to_wrap)
    def uni_wrapper(*args, **kwargs):
        if from_coroutine():
            return async_wrapper(*args, **kwargs)
        time.sleep(.1)
        print('Did some sync stuff')
        return fn_to_wrap(*args, **kwargs)