2015-05-25 4 views
4

В настоящее время я использую python 3.4, декоратор @coroutine и ключевое слово yield (в торнадо) для асинхронных целей. Я написал ORM с множеством объектов instrospection, которые вызывают «медленную» базу данных и переопределяют встроенные функции, такие как __init__ или __contains__. Мой вопрос: Например, когда мой асинхронный код находится в определении объекта __contains__, как его можно косвенно/прозрачно вызвать, когда я использую оператор «in» в моем контроллере торнадо? Неявно, потому что я не хочу, чтобы разработчик на стороне контроллера менял свой код, когда он вызывал встроенные функции.Python coroutines on встроенные функции

+0

Можете ли вы показать нам пример кода, который демонстрирует то, что вы спрашиваете? Этот вопрос немного неясен. –

ответ

7

Если я правильно понимаю вопрос, ответ заключается в том, что вы не можете; нет способа написать магический метод в качестве явной сопрограммы и вести себя корректно, если он неявно называется 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 
Смежные вопросы