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
- 如果包装器作者不控制最终类,则自定义类属性将不起作用
,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)