В настоящее время я использую python 3.4, декоратор @coroutine и ключевое слово yield (в торнадо) для асинхронных целей. Я написал ORM с множеством объектов instrospection, которые вызывают «медленную» базу данных и переопределяют встроенные функции, такие как __init__
или __contains__
. Мой вопрос: Например, когда мой асинхронный код находится в определении объекта __contains__
, как его можно косвенно/прозрачно вызвать, когда я использую оператор «in» в моем контроллере торнадо? Неявно, потому что я не хочу, чтобы разработчик на стороне контроллера менял свой код, когда он вызывал встроенные функции.Python coroutines on встроенные функции
ответ
Если я правильно понимаю вопрос, ответ заключается в том, что вы не можете; нет способа написать магический метод в качестве явной сопрограммы и вести себя корректно, если он неявно называется Python. Это well-knownlimitation явных сопрограмм.
Так что, если у вас есть это:
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__
быть сопрограммная, и вона 't вести себя правильно, если это так - ядро языка ничего не знает о tornado
, или asyncio
, или каких-либо других фреймворках, используемых для реализации этих сопрограмм, и не будет правильно интегрироваться с ними. То же самое относится и к другим неявно называемым магическим методам, таким как __init__
, __getattr__
и т.д.
Если вам необходимо приостановить, вы должны явно вызвать метод с использованием yield
или yield from
(в зависимости от структуры). Как правило, это означает, что с помощью функции (или, возможно, @classmethod
), чтобы создать экземпляр SomeObj
, а затем с, что функция вызова метода, который делает медленный, асинхронный вызов, а не делать все это в __init__
:
@coroutine
def create_someobj():
s = SomeObj()
yield s.slow_init()
return s
и просто вызывая обычный метод coroutine, называемый как-то вроде contains
, вместо того, чтобы полагаться на ключевое слово in
. Не идеальный, но это тот мир, в котором мы живем.
Это говорит о том, что прилагаются некоторые усилия для улучшения этого; PEP 492, помимо введения нового синтаксиса для сопрограмм, добавляет поддержку асинхронных for-loops и контекстных менеджеров (с использованием новых магических методов, специально разработанных для асинхронности). Итак, начиная с 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
Можете ли вы показать нам пример кода, который демонстрирует то, что вы спрашиваете? Этот вопрос немного неясен. –