我想实现一个等待的,并注意到__await__
'需要'成为一个生成器。
来自PEP-492:
使用
__await__
方法返回迭代器的对象。...
具有
__await__
方法的对象在本PEP的其余部分中称为类似Future的对象。如果
__await__
返回除迭代器之外的任何内容,则为TypeError。
根据我的经验,在await
作为声明之前,yield from
与作为生成器实现的协同程序一起使用。现在python(我使用3.5)使用async def
语法的异步方法。因此,我认为yield from
语法为旧/已弃用。
所以我打破了解释器,看看它是如何工作的:
>>> class A:
... def __await__(self):
... yield from (asyncio.sleep(1).__await__())
... return 'spam'
...
>>> a = A()
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.5/asyncio/base_events.py", line 467, in run_until_complete
return future.result()
File "/usr/lib64/python3.5/asyncio/futures.py", line 294, in result
raise self._exception
File "/usr/lib64/python3.5/asyncio/tasks.py", line 240, in _step
result = coro.send(None)
File "/usr/lib64/python3.5/asyncio/tasks.py", line 585, in _wrap_awaitable
return (yield from awaitable.__await__())
File "<stdin>", line 3, in __await__
AttributeError: 'generator' object has no attribute '__await__'
所以看来asyncio.sleep
没有__await__
方法。使用这种yield from
语法也感觉非常尴尬。
所以我决定尝试使用async
语法,只是为了看看它是否可行:
>>> class A:
... async def __await__(self):
... await asyncio.sleep(1)
... return 'spam'
...
>>> a = A()
>>>
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(a)
'spam'
它实际上似乎有效!所以现在我想知道,__await__
方法真的需要是否是使用yield from
语法的生成器?
编辑:添加间接级别时,所以在await
语句中使用等待的问题变得明显:
>>> async def test():
... return await A()
...
>>> loop.run_until_complete(test())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "<stdin>", line 2, in test
TypeError: __await__() returned a coroutine
因此它实际上需要像这样返回一个生成器:
class A:
def __await__(self):
yield from asyncio.sleep(1)
return 'spam'
答案 0 :(得分:2)
所以看来
asyncio.sleep
没有__await__
方法
是的,但它并不一定要等待。 documentation表示__await__
(如果存在)需要返回迭代器,而不是await
仅适用于定义__await__
的对象。实际上,它明确地证明await
的参数可以是 中的一个:
从原生协程函数返回的 native coroutine 对象。
从装饰有types.coroutine()
的函数返回的基于生成器的协同程序对象。
使用__await__
方法返回迭代器的对象。
在C中定义的对象,提供与__await__
特殊方法等效的Python / C。
所以现在我想知道,
__await__
方法真的需要使用语法中的yield吗?
如果你实际上有一个__await__
方法,它确实需要返回一个迭代器。
答案 1 :(得分:0)
要使用await
表达式,__await__
不必是生成器。但是,只有__await__
的结果支持生成器接口时,某些操作才可用。
即,不可能将send
个值或throw
例外放入迭代器__await__
中。只能None
“发送”到迭代器__await__
,就像使用了generator.__next__
一样。
让我们考虑一个简单的Awaitable
,它从其__await__
返回一个迭代器。
class Iter:
"""Basic iterator that yields the same value"""
def __next__(self): return 1
def __iter__(self): return self
class IterAwait:
"""Awaitable that uses an iterator for __await__"""
def __await__(self):
return Iter()
我们可以检查它们是否实现了所需的接口:
>>> from collections.abc import Awaitable, Iterator, Generator
>>> isinstance(IterAwait(), Awaitable)
True
>>> isinstance(IterAwait().__await__(), Iterator)
True
>>> isinstance(IterAwait().__await__(), Generator)
False
为了查看它如何与await
交互,我们将其包装在协程中:
async def iter_await():
await IterAwait()
我们在iter_await
上使用完整的协程/生成器接口执行的所有操作都由await
转发到我们的迭代器-__await__
。这样可以研究迭代器__await__
如何接收信号:
>>> test_iter = iter_await()
>>> test_iter.send(3) # 0. does it appear like a coroutine?
TypeError: can`t send non-None value to a just-started coroutine
>>> test_iter.send(None) # 1. must initialise just-started coroutine
1
>>> test_iter.send(None) # 2. repeatedly use the underlying iterator
1
>>> next(test_iter) # 3. do we expose the iterator?
TypeError: 'coroutine' object is not an iterator
>>> test_iter.send(3) # 4. can we send non-None values?
AttributeError: 'Iter' object has no attribute 'send'
>>> test_iter = iter_await() # we just broke the coroutine...
>>> test_iter.send(None) # ...need a new one
1
>>> test_iter.throw(KeyError) # 4. can we throw Exceptions?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in iter_await
KeyError
可以看出,await
可以处理迭代器-__await__
,但不能转发所有操作。但是,有些已翻译,有些已提早处理。
.send(None)
始终是可能的,它已翻译为裸露的__next__()
。 (1、2).__next__
(3),也无法翻译.send
并带有值(4)。.throw
例外,但await
在协程中尽早处理它。请注意,await
使用throw
和send
方法。如果__await__
的结果实现了send
但没有实现throw
,反之亦然,则使用存在的功能。只有__next__
是必填项。