Python 内置函数的协程
Python coroutines on builtin functions
我目前正在使用 python 3.4,@coroutine 装饰器和 yield 关键字(在龙卷风中)用于异步目的。
我写了一个带有很多对象内省的 ORM,它调用 "slow" 数据库并覆盖内置函数,如 __init__
或 __contains__
。
我的问题是:
例如,当我的异步代码在对象的 __contains__
定义中时,当我在 tornado 控制器中使用 "in" 运算符时,如何调用它 implicitly/transparently ?
隐含的,因为我不希望控制器端开发人员在调用内置函数时更改他的代码。
如果我正确理解问题,答案是你不能;没有办法将魔术方法编写为显式协程,并使其在被 Python 隐式调用时正常运行。这是一个 well-known limitation 显式协程。
所以,如果你有这个:
class SomeObj:
@coroutine
def __contains__(self, obj):
exists = yield self.somemethod(obj)
return exists
这不会执行您希望它执行的操作:
o = SomeObj()
'x' in o # This won't work right
Python 不希望 __contains__
成为协程,如果是协程则不会正常运行 - 语言的核心对 tornado
一无所知,或 asyncio
,或用于实现这些协程的任何其他框架,并且不会与它们正确集成。这同样适用于其他隐式调用的魔术方法,如 __init__
、__getattr__
等
如果需要暂停,则必须使用 yield
或 yield from
(取决于框架)显式调用方法。通常,这意味着使用一个函数(或者可能是一个 @classmethod
)来实例化您的 SomeObj
,然后让该函数调用一个执行缓慢的异步调用的方法,而不是在 [=17] 中完成所有操作=]:
@coroutine
def create_someobj():
s = SomeObj()
yield s.slow_init()
return s
并且只调用一个像 contains
这样的普通协程方法,而不是依赖 in
关键字。不理想,但这就是我们生活的世界。
也就是说,我们正在努力改善这一点; PEP 492,除了为协程引入新语法外,还增加了对异步 for 循环和上下文管理器的支持(使用专门为异步设计的新魔术方法)。所以从 Python 3.5 开始,你可以这样:
async def some_func(): # async is used instead of a coroutine decorator
# Assume SomeObj implements __anext__, __aiter__, __aenter__, and __aexit__
s = SomeObj()
async for item in s: # You can suspend while iterating over s using __anext__
print(item)
async with SomeObj() as s: # You can suspend on enter and exit of this context manager using __aenter__ and __aexit__
await s.some_method() # await is used instead of yield from
我目前正在使用 python 3.4,@coroutine 装饰器和 yield 关键字(在龙卷风中)用于异步目的。
我写了一个带有很多对象内省的 ORM,它调用 "slow" 数据库并覆盖内置函数,如 __init__
或 __contains__
。
我的问题是:
例如,当我的异步代码在对象的 __contains__
定义中时,当我在 tornado 控制器中使用 "in" 运算符时,如何调用它 implicitly/transparently ?
隐含的,因为我不希望控制器端开发人员在调用内置函数时更改他的代码。
如果我正确理解问题,答案是你不能;没有办法将魔术方法编写为显式协程,并使其在被 Python 隐式调用时正常运行。这是一个 well-known limitation 显式协程。
所以,如果你有这个:
class SomeObj:
@coroutine
def __contains__(self, obj):
exists = yield self.somemethod(obj)
return exists
这不会执行您希望它执行的操作:
o = SomeObj()
'x' in o # This won't work right
Python 不希望 __contains__
成为协程,如果是协程则不会正常运行 - 语言的核心对 tornado
一无所知,或 asyncio
,或用于实现这些协程的任何其他框架,并且不会与它们正确集成。这同样适用于其他隐式调用的魔术方法,如 __init__
、__getattr__
等
如果需要暂停,则必须使用 yield
或 yield from
(取决于框架)显式调用方法。通常,这意味着使用一个函数(或者可能是一个 @classmethod
)来实例化您的 SomeObj
,然后让该函数调用一个执行缓慢的异步调用的方法,而不是在 [=17] 中完成所有操作=]:
@coroutine
def create_someobj():
s = SomeObj()
yield s.slow_init()
return s
并且只调用一个像 contains
这样的普通协程方法,而不是依赖 in
关键字。不理想,但这就是我们生活的世界。
也就是说,我们正在努力改善这一点; PEP 492,除了为协程引入新语法外,还增加了对异步 for 循环和上下文管理器的支持(使用专门为异步设计的新魔术方法)。所以从 Python 3.5 开始,你可以这样:
async def some_func(): # async is used instead of a coroutine decorator
# Assume SomeObj implements __anext__, __aiter__, __aenter__, and __aexit__
s = SomeObj()
async for item in s: # You can suspend while iterating over s using __anext__
print(item)
async with SomeObj() as s: # You can suspend on enter and exit of this context manager using __aenter__ and __aexit__
await s.some_method() # await is used instead of yield from