http://habrahabr.ru/post/270709/
Это достаточно вольный перевод статьи об основных новшествах асинхронного драйвера для mongodb
используемого в tornado
. Основной мотив, который послужил для написания этого перевода — новшества, появившиеся в этой версии, такие как поддержка asyncio
, async
, await
и Python 3.5
. Сама статья не сколько перечисление новшеств, сколько лаконичные примеры асинхронной работы с MongoDB
.
Введение asyncio aggregate Python 3.5 async and awaitВведение
Недавно была опубликована новая
Beta
версия
Python
драйвера для
Mongodb
,
Motor
. В этой версии содержится одно из самых больших обновлений. Для установки можно использовать:
python -m pip install --pre motor==0.5b0
Motor 0.5
по прежнему зависит от
PyMongo 2.8.0
. Это устаревшая версия
PyMongo
, но сейчас не было достаточно времени чтоб полностью перейти на третью версию, что простительно, так как этот релиз достаточно большой.
asyncio
Motor
теперь может интегрироваться с
asyncio
, как альтернатива
Tornado
. Большая благодарность Реми Джолину, Андрею Светлову
svetlov и Николаю Новику за их огромный вклад в интеграцию
Motor
для работы с
asyncio
.
API-Интерфейсы
Tornado
и
asyncio
являются родственными. Пример
Motor
с
Tornado
:
# Tornado API
from tornado import gen, ioloop
from motor.motor_tornado import MotorClient
@gen.coroutine
def f():
result = yield client.db.collection.insert({'_id': 1})
print(result)
client = MotorClient()
ioloop.IOLoop.current().run_sync(f)
И здесь пример для asyncio:
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
@asyncio.coroutine
def f():
result = yield from client.db.collection.insert({'_id': 1})
print(result)
client = AsyncIOMotorClient()
asyncio.get_event_loop().run_until_complete(f())
В отличие от
Tornado
,
asyncio
не включает реализацию
http
, а тем более не является фреймворком. Для этого используйте библиотеку
aiohttp
Андрея Светлова. Небольшой
пример для работы Motor с aiohttp.
aggregate
MotorCollection.aggregate теперь по умолчанию возвращает курсор, и курсор возвращается непосредственно без
yield
. Старый синтаксис больше не поддерживается:
# Motor 0.4 and older, no longer supported.
cursor = yield collection.aggregate(pipeline, cursor={})
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
В
Motor 0.5
просто сделайте:
# Motor 0.5: no "cursor={}", no "yield".
cursor = collection.aggregate(pipeline)
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
В
asyncio
для этого используется
yield from
:
# Motor 0.5 with asyncio.
cursor = collection.aggregate(pipeline)
while (yield from cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Python 3.5
Сейчас
Motor
совместим с
Python 3.5
, что потребовало определённых усилий. Это было трудно, потому что
Motor
не просто работает с сопрограммами (coroutines), он использует сопрограммы внутри себя для реализации некоторых из своих функций, таких как
MotorClient.open и
MotorGridFS.put.
Был метод для написания сопрограмм, которые работают в
Python 2.6
c
Python 3.4
, но в
Python 3.5
это было окончательно поломано. Нет единого пути для возвращения значений из
Python 3.5
нативной сопрограмме или
Python 2
генератора базирующегося на сопрограмме, так что все внутренние сопрограммы
motor
, которые возвращают значения, были переписаны с помощью обратных вызовов.
async and await
Награда за усилия потраченные на интеграцию с
Python 3.5
, состоит в том что теперь
motor
работает с родной сопрограммой, написанной с учетом ключевых слов
async
и
await
синтаксис:
async def f():
await collection.insert({'_id': 1})
Курсор из
MotorCollection.find,
MotorCollection.aggregate, или
MotorGridFS.find может быть красиво и очень эффективно итегрирован в нативных сопрограммах (coroutines) с
async for
:
async def f():
async for doc in collection.find():
print(doc)
Насколько эффективно? Для коллекции из 10 000 документов этот пример кода выполнялся за 0.14 секунды.
# Motor 0.5 with Tornado.
@gen.coroutine
def f():
cursor = collection.find()
while (yield cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Следующий код, в котором просто заменены
gen.coroutine
и
yield
на
async
и
await
и выполняет примерно тоже.
# Motor 0.5 with Tornado, using async and await.
async def f():
cursor = collection.find()
while (await cursor.fetch_next):
doc = cursor.next_object()
print(doc)
Но с
async for
время работы занимает 0.04 секунды, то есть в три раза быстрее.
# Motor 0.5 with Tornado, using async for.
async def f():
cursor = collection.find()
async for doc in cursor:
print(doc)
Однако, MotorCursor в
to_list прежнему играет основную роль:
# Motor 0.5 with Tornado, using to_list.
async def f():
cursor = collection.find()
docs = await cursor.to_list(length=100)
while docs:
for doc in docs:
print(doc)
docs = await cursor.to_list(length=100)
Функция с
to_list
в два раза быстрее чем асинхронные, но это выглядит не так красиво и требует указания размера чанка. Я думаю, что
async for
выглядит довольно стильно и работает достаточно быстро для того, чтобы его применять в большинстве случаев.
Бета версии релизов
motor
публиковались далеко не всегда, но в этот раз по-другому. Интеграция
asyncio
в
motor
является совершенно новой. И поскольку это потребовало повсеместного рефакторинга ядра
motor
, и переписывания существующей интеграции
tornado
, была выпущена бета-версия для того, чтобы исправить все упущения.
P.S. Просьба о грамматических ошибках и ошибках перевода писать в личку.