比方说,我在Python 3.6中拥有一个神奇的方法,例如:
def __str__(self):
return 'a {self.color} car'.format(self=self)
现在我想在其中添加一个协程,例如:
async def __str__(self):
await self.my_coro(args)
return 'a {self.color} car'.format(self=self)
据我所知,有必要在def之前添加async,但这似乎不适用于魔术方法。有(简单的)解决方法还是完全不可能?
答案 0 :(得分:2)
TLDR:没有解决方法,因为Python期望许多特殊方法使用特定类型。但是,某些特殊方法具有异步变体。
async def
函数本质上是另一种函数。类似于生成器函数对生成器求值的方式,协程函数对等待器求值。
您可以将特殊方法定义为异步方法,但这会影响它们的返回类型。对于__str__
,您得到的是Awatable[str]
而不是裸露的str
。由于Python在这里需要 一个str
,因此会导致错误。
>>> class AStr:
... async def __str__(self):
... return 'Hello Word'
>>> str(AStr())
TypeError: __str__ returned non-string (type coroutine)
这会影响所有直接由Python解释的特殊方法:其中包括__str__
,__repr__
,__bool__
,__len__
,__iter__
,{{1 }}和其他一些。通常,如果一种特殊方法与某些内部使用的功能(例如,用于直接显示的__enter__
)或语句(例如,需要迭代器的str
)有关,则不能为for
。
Python不会直接解释某些特殊方法。示例包括算术运算符(async
,__add__
,...),比较(__sub__
,__lt__
,...)和查找(__eq__
,{ {1}},...)。它们的返回类型可以是任何对象,包括等待对象。
您可以通过__get__
定义此类特殊方法。这会影响它们的返回类型,但只需要客户端代码__getattribute__
。例如,您可以定义async def
用作await
。
+
await (a + b)
机制中存在一些特殊方法,这些方法应返回一个等待的方法。这包括>>> def AWAIT(awaitable):
... """Basic event loop to allow synchronous ``await``"""
... coro = awaitable.__await__()
... try:
... while True:
... coro.send(None)
... except StopIteration as e:
... return e.args[0] if e.args else None
...
>>> class APlus:
... def __init__(self, value):
... self.value = value
... async def __add__(self, other):
... return self.value + other
...
>>> async def add(start, *values):
... total = start
... for avalue in map(APlus, values):
... total = await (avalue + total)
... return total
...
>>> AWAIT(add(5, 10, 42, 23))
80
,await
和__aenter__
。值得注意的是,__aexit__
必须返回一个迭代器,而不是等待的迭代器。
您可以(在大多数情况下应该)将这些方法定义为__anext__
。如果需要在相应的同步特殊方法中使用异步功能,请将相应的异步特殊方法与__await__
一起使用。例如,您可以定义一个异步上下文管理器。
async def