Это достаточно вольный перевод статьи об основных новшествах асинхронного драйвера для 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 секунды (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 секунды (4 миллисекунды), то есть в три раза быстрее.
# 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. Просьба о грамматических ошибках и ошибках перевода писать в личку.
ссылка на оригинал статьи http://habrahabr.ru/post/270709/
Добавить комментарий