__await__ 是否需要成为生成器?
Does __await__ need to be a generator?
我想实现一个 awaitable 并注意到 __await__
'needs' 是一个生成器。
来自 PEP-492:
An object with an __await__
method returning an iterator.
...
Objects with __await__
method are called Future-like objects in the rest of this PEP.
It is a TypeError if __await__
returns anything but an iterator.
根据我的经验,在 await
是一个语句之前,yield from
与作为生成器实现的协程一起使用。如今 python(我使用的是 3.5)具有使用 async def
语法的异步方法。因此,我将 yield from
语法视为 old/deprecated.
所以我打开了解释器来查看 how/if 这行得通:
>>> 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'
So it appears asyncio.sleep
doesn't have the __await__
method
是的,但不一定要等待。 documentation 说 __await__
,如果存在,需要 return 一个迭代器,而不是说 await
只适用于定义了 __await__
的对象。事实上,它明确记录了 await
的参数可以是 之一:
本机协程对象return从本机协程函数编辑。
一个 generator-based 协程 对象 return 从装饰有 types.coroutine()
.
的函数中编辑
具有 __await__
方法的对象 return 迭代器。
C 中定义的对象,提供 __await__
特殊方法的 Python/C 等价物。
So now I'm wondering, does the __await__
method really need to be a generator using the yield from syntax?
如果您确实有一个 __await__
方法,它确实需要 return 一个迭代器。
为了在 await
表达式中工作,__await__
不需要是生成器。但是,某些操作仅在 __await__
的结果支持生成器接口时才可用。
即,无法将 send
值或 throw
异常放入迭代器 -__await__
。只有 None
可以 "sent" 到迭代器-__await__
,就像使用 generator.__next__
一样。
让我们考虑一个简单的 Awaitable
,returns 来自其 __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()
我们使用完整的 coroutine/generator 接口在 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__
是强制性的。
我想实现一个 awaitable 并注意到 __await__
'needs' 是一个生成器。
来自 PEP-492:
An object with an
__await__
method returning an iterator....
Objects with
__await__
method are called Future-like objects in the rest of this PEP.It is a TypeError if
__await__
returns anything but an iterator.
根据我的经验,在 await
是一个语句之前,yield from
与作为生成器实现的协程一起使用。如今 python(我使用的是 3.5)具有使用 async def
语法的异步方法。因此,我将 yield from
语法视为 old/deprecated.
所以我打开了解释器来查看 how/if 这行得通:
>>> 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'
So it appears
asyncio.sleep
doesn't have the__await__
method
是的,但不一定要等待。 documentation 说 __await__
,如果存在,需要 return 一个迭代器,而不是说 await
只适用于定义了 __await__
的对象。事实上,它明确记录了 await
的参数可以是 之一:
本机协程对象return从本机协程函数编辑。
一个 generator-based 协程 对象 return 从装饰有
的函数中编辑types.coroutine()
.具有
__await__
方法的对象 return 迭代器。C 中定义的对象,提供
__await__
特殊方法的 Python/C 等价物。
So now I'm wondering, does the
__await__
method really need to be a generator using the yield from syntax?
如果您确实有一个 __await__
方法,它确实需要 return 一个迭代器。
为了在 await
表达式中工作,__await__
不需要是生成器。但是,某些操作仅在 __await__
的结果支持生成器接口时才可用。
即,无法将 send
值或 throw
异常放入迭代器 -__await__
。只有 None
可以 "sent" 到迭代器-__await__
,就像使用 generator.__next__
一样。
让我们考虑一个简单的 Awaitable
,returns 来自其 __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()
我们使用完整的 coroutine/generator 接口在 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__
是强制性的。