Сегодня хороший день: я опубликовал бета-версию Motor , моего асинхронного драйвера Python для MongoDB. Эта версия — самое большое обновление. Помоги мне бета-тестирование! Установить с помощью:
python -m pip install --pre motor==0.5b0
Мотор 0.5 все еще зависит от PyMongo 2.8.0 точно. Я знаю, что эта версия PyMongo устарела, но я решил не заниматься этой проблемой прямо сейчас.
Вы меня простите, потому что этот моторный релиз огромен:
Asyncio
Мотор теперь может интегрироваться с Asyncio, как альтернатива Tornado. Я благодарен Реми Жолину, Андрею Светлову и Николаю Новику за их огромный вклад в интеграцию асинцио Мотор.
API Торнадо и Asyncio являются родственными. Вот Мотор с Торнадо:
# 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 .
заполнитель
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)
В Мотор 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
Мотор теперь совместим с Python 3.5, что потребовало некоторых усилий. Это было сложно, потому что Motor не просто работает с вашими сопрограммами, он использует сопрограммы внутри себя для реализации некоторых своих собственных функций, таких как MotorClient.open
и MotorGridFS.put
. У меня был метод для написания сопрограмм, который работал в Python 2.6 — 3.4, но 3.5 наконец сломал его. Не существует единого способа вернуть значение из собственной сопрограммы Python 3.5 или сопрограммы на основе генератора Python 2, поэтому все внутренние сопрограммы Motor, возвращающие значения, были переписаны с помощью обратных вызовов. (См. Сообщение фиксации dc19418c для объяснения.)
Async и Await
Это награда за мои усилия в Python 3.5. Мотор работает с носителями сопрограмм, написанных с async
и await
синтаксисом:
async def f():
await collection.insert({'_id': 1})
Курсоры из MotorCollection.find
, MotorCollection.aggregate
или MotorGridFS.find
могут быть элегантно и очень эффективно повторены в нативных сопрограммах с 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
, но она не изящна и требует от вас выбора размера чанка. Я думаю, что async for
это стильно и достаточно быстро для большинства применений.
Попробуй меня!
Я не всегда публиковал бета-версии перед выпуском Motor, но на этот раз все по-другому. Интеграция Asyncio является совершенно новым. А поскольку требовался повсеместный рефакторинг ядра Motor, существующая интеграция Tornado также была переписана. Поддержка Python 3.5 требует еще одного внутреннего пересмотра. Я очень хочу получить ранние отчеты обо всем моем новом коде в дикой природе.
Кроме того, изменение aggregate
является разрывом API. (Есть также два более тонких изменения, см. Список изменений .) Так что я даю вам возможность явного выбора, pip install --pre
прежде чем я сделаю Motor 0.5 официальным.
Пожалуйста, попробуйте! Установите бета-версию:
python -m pip install --pre motor==0.5b0
Протестируйте свое приложение с новым кодом. Если вы обнаружите проблемы, сообщите об ошибке, и я быстро отвечу. И если бета пройдет гладко, не молчите ! Это единственный способ узнать, что бета работает на вас.