Если вы хоть раз задумывались о том, можно ли фичи FastAPI (автогенерация OpenAPI, интеграция с Pydantic, поддержка асинхронности и т.д.) добавить в Django, то я пришел вас обрадовать - есть такой проект под названием django-ninja.
Из приятных фич, которые можно встретить здесь:
1) Версионирование и возможность создания нескольких API инстансов со своей авторизацией и т.д.
2) Класс схемы интегрирован с модельками Django, поэтому можно писать что-то вроде такого:
4) Пагинация! При чем в документации показано, как можно сделать собственный кастомный класс для неё.
5) Поддержка кастомных рендереров ответов, что позволяет перевести сериализацию на ORJSON или отдавать ответы в XML, например.
6) Работа с аутентификацией из под коробки.
7) Ну и асинхронность, тем более разработчики Django работают над ней.
А в остальном он очень похож на FastAPI, особенно когда речь идет о работе с аргументами, схемами и OpenAPI.
Как по мне, выглядит как достойная замена DRF, при чем довольно простая. И об этом я говорю не просто так - в последнее время я делал проекты на FastAPI и возвращаться на Django и тем более DRF было очень непривычно.
Github | Документация
#django #библиотека
Из приятных фич, которые можно встретить здесь:
1) Версионирование и возможность создания нескольких API инстансов со своей авторизацией и т.д.
2) Класс схемы интегрирован с модельками Django, поэтому можно писать что-то вроде такого:
@api.get("/tasks", response=List[TaskSchema])3) Можно делать схемы из моделей, прям как в DRF.
def tasks(request):
return Task.objects.all()
4) Пагинация! При чем в документации показано, как можно сделать собственный кастомный класс для неё.
5) Поддержка кастомных рендереров ответов, что позволяет перевести сериализацию на ORJSON или отдавать ответы в XML, например.
6) Работа с аутентификацией из под коробки.
7) Ну и асинхронность, тем более разработчики Django работают над ней.
А в остальном он очень похож на FastAPI, особенно когда речь идет о работе с аргументами, схемами и OpenAPI.
Как по мне, выглядит как достойная замена DRF, при чем довольно простая. И об этом я говорю не просто так - в последнее время я делал проекты на FastAPI и возвращаться на Django и тем более DRF было очень непривычно.
Github | Документация
#django #библиотека
django-ninja.dev
Django Ninja
Django Ninja - Django REST framework with high performance, easy to learn, fast to code.
Немного про роутинг в FastAPI
Если вы пользовались FastAPI, то наверняка знаете, что роут можно сделать либо асинхронным, либо синхронным. Так когда какой надо делать?
Скорее всего первая мысль которая придет вам в голову будет звучать как-то так - если у нас есть I/O-bound задачи (например работа с БД), то надо использовать асинхронщину, если всё остальное - потоки, процессы и так далее. Но тут есть несколько нюансов:
1) Под капотом FastAPI отлично справляется с обработкой как синхронных, так и асинхронных роутов. Если роут асинхронный, то задача по его обработке запустится в
2) Так как синхронные роуты запускаются в
Возьмем вот такой роут:
А теперь возьмем вот такой роут:
Поэтому, если вам нужно написать на FastAPI небольшой CRUD и вы думаете тащить асинхронную ORM - задумайтесь, а надо ли она вам там вообще?
Ссылки:
- Path operation functions
#fastapi
Если вы пользовались FastAPI, то наверняка знаете, что роут можно сделать либо асинхронным, либо синхронным. Так когда какой надо делать?
Скорее всего первая мысль которая придет вам в голову будет звучать как-то так - если у нас есть I/O-bound задачи (например работа с БД), то надо использовать асинхронщину, если всё остальное - потоки, процессы и так далее. Но тут есть несколько нюансов:
1) Под капотом FastAPI отлично справляется с обработкой как синхронных, так и асинхронных роутов. Если роут асинхронный, то задача по его обработке запустится в
event loop
, если синхронный - то в thread pool
.2) Так как синхронные роуты запускаются в
thread pool
, иногда просто нет вообще никакого смысла тащить в проект асинхронную ORM, так как всё и так будет работать не блокируя основное приложение.Возьмем вот такой роут:
@router.get("/nonblocking-sync-operation")После того как мы перейдем по этому роуту, мы будем ждать 10 секунд и в конце получим ответ. При этом сам FastAPI не заблокируется, и сможет обрабатывать другие подключения - потому что функция запустилась в отдельном потоке.
def nonblocking_sync_operation():
time.sleep(10)
return {"test": "test"}
А теперь возьмем вот такой роут:
@router.get("/blocking-sync-operation")Здесь после перехода по роуту функция запустится в event loop и sleep заблокирует всё приложение до тех пор, пока он не пройдет. То есть, FastAPI вообще перестанет принимать подключения до тех пор, пока функция не выполнится.
async def blocking_sync_operation():
time.sleep(10)
return {"test": "test"}
Поэтому, если вам нужно написать на FastAPI небольшой CRUD и вы думаете тащить асинхронную ORM - задумайтесь, а надо ли она вам там вообще?
Ссылки:
- Path operation functions
#fastapi
В PEP 695 предлагают наконец переделать синтаксис для указания дженериков и ввести новый оператор для указания алиасов.
1) Что там с дженериками?
Например, если раньше было так:
Раньше алиасы типов записывались вот так:
#pep
1) Что там с дженериками?
Например, если раньше было так:
_T_co = TypeVar("_T_co", covariant=True, bound=str)То сейчас предлагают сделать так:
class ClassA(Generic[_T_co]):
def method1(self) -> _T_co:
...
class ClassA[T: str]:2) А что с алиасами?
def method1(self) -> T:
...
Раньше алиасы типов записывались вот так:
_T = TypeVar("_T")Сейчас предлагают сделать вот так:
ListOrSet: TypeAlias = list[_T] | set[_T]
type ListOrSet[T] = list[T] | set[T]Если не ошибаюсь, то ждем в 3.12, PEP уже приняли.
#pep
Python Enhancement Proposals (PEPs)
PEP 695 – Type Parameter Syntax | peps.python.org
This PEP specifies an improved syntax for specifying type parameters within a generic class, function, or type alias. It also introduces a new statement for declaring type aliases.
Очень годный доклад о том как начинать проекты на Django.
Рассмотрели нужность батареек, как правильно писать логику и что там на самом деле с админкой и DRF.
#django #посмотреть
Рассмотрели нужность батареек, как правильно писать логику и что там на самом деле с админкой и DRF.
#django #посмотреть
YouTube
Как и зачем начинать проекты на Django в 2021 году / Фёдор Борщёв
Приглашаем на Moscow Python Conf 2023, которая пройдет 19 и 20 мая 2023 в Москве в рамках Positive Hack Days.
Программа, подробности и билеты по ссылке https://conf.python.ru/moscow/2023
--------
Moscow Python Conf++ 2021
Профессиональная конференция для…
Программа, подробности и билеты по ссылке https://conf.python.ru/moscow/2023
--------
Moscow Python Conf++ 2021
Профессиональная конференция для…
Один из моих любимых докладов о том, почему вам не нужен асинхронный ORM.
В докладе автор рассказывает про то, что происходит под капотом у асинхронной алхимии, поэтому рекомендуется к просмотру тем, кто пишет бекенд.
#sqlalchemy #посмотреть
В докладе автор рассказывает про то, что происходит под капотом у асинхронной алхимии, поэтому рекомендуется к просмотру тем, кто пишет бекенд.
#sqlalchemy #посмотреть
YouTube
Почему вам не нужен асинхронный ORM / Денис Катаев
Приглашаем на Moscow Python Conf 2023, которая пройдет 19 и 20 мая 2023 в Москве в рамках Positive Hack Days.
Программа, подробности и билеты по ссылке https://conf.python.ru/moscow/2023
--------
Moscow Python Conf++ 2021
Профессиональная конференция для…
Программа, подробности и билеты по ссылке https://conf.python.ru/moscow/2023
--------
Moscow Python Conf++ 2021
Профессиональная конференция для…
Оказывается в конце апреля urllib3 обновили до 2.0.0, который делали аж с 2020 года. Из очень годного:
1) Сделали функцию
2) Упростили работу с json, теперь все выглядит следующим образом:
3) Тайпинги! Хотя, было бы странно видеть такого вида библиотеку без тайпингов в 2023 году.
Плюс ко всему команда описала, с какими проблемами они столкнулись когда добавляли тайпинги, почитать здесь.
Ссылка на новость
1) Сделали функцию
urllib3.request()
, прям как в всеми любимом requests
. Теперь работать с ним можно следующим образом:response = urllib3.request("GET", "https://example.com")И никаких тебе больше
urlopen
.2) Упростили работу с json, теперь все выглядит следующим образом:
resp = urllib3.request(Всё как у
"POST", "https://httpbin.org/anything",
# Параметр json здесь заэнкодит json в body запроса
# и установит 'Content-Type' в 'application/json'.
json={"key": "value"}
)
# а вот так теперь можно получить наш жсончк c ответа
print(resp.json())
requests
, всё как у людей.3) Тайпинги! Хотя, было бы странно видеть такого вида библиотеку без тайпингов в 2023 году.
Плюс ко всему команда описала, с какими проблемами они столкнулись когда добавляли тайпинги, почитать здесь.
Ссылка на новость
Docker 4.19 теперь поддерживает python в docker init
docker init - это утилита, которая упрощает добавление docker в проект. Она просто пробежит по вопросам, спросит версию проекта, какой командой он запускается и сгенерирует Dockerfile и compose-файл.
Мелочь, а приятно :)
#docker
docker init - это утилита, которая упрощает добавление docker в проект. Она просто пробежит по вопросам, спросит версию проекта, какой командой он запускается и сгенерирует Dockerfile и compose-файл.
Мелочь, а приятно :)
#docker
Docker Documentation
docker init
Попался на глаза отличный перевод статьи "Writing Python like it's Rust", который скорее говорит о возможностях аннотаций типов и структур которые их используют (и еще немного про контекстные менеджеры).
Очень много примеров и очень много полезных советов :)
#статья #хабр
Очень много примеров и очень много полезных советов :)
#статья #хабр
Хабр
Пишем на Python как на Rust
Я начал программировать на Rust несколько лет назад, и это постепенно изменило мой подход к разработке программ на других языках программирования, особенно на Python. До того, как я начал использовать...
Если обычного itertools вам мало, то можно использовать more-itertools.
Эта библиотека добавляет огромное количество функций для работы с итераторами. На практике всеми ими не всегда пользуются, поэтому я выделю некоторые из тех, которые сам использую часто.
Например, вот так можно разделить список на 3 части:
Решим самую частую проблему - перевести список с несколькими уровнями вложенностями в "плоский" список:
PyPI | Документация
#more_itertools #itertools #библиотека #рецепт
Эта библиотека добавляет огромное количество функций для работы с итераторами. На практике всеми ими не всегда пользуются, поэтому я выделю некоторые из тех, которые сам использую часто.
Например, вот так можно разделить список на 3 части:
data = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh"]Вот еще задача. Надо разделить список с элементами по определенному условию. В этом нам поможет
[list(l) for l in divide(3, data)]
# [['first', 'second', 'third'], ['fourth', 'fifth'], ['sixth', 'seventh']]
bucket
:class Cat:По итогу кошки и собаки будут разделены по своему типу на 2 генератора.
pass
class Dog:
pass
shapes = [Cat(), Dog(), Cat(), Dog(), Cat(), Cat()]
result = more_itertools.bucket(shapes, key=lambda x: type(x))
len(list(result[Cat])) # 4
len(list(result[Dog])) # 2
Решим самую частую проблему - перевести список с несколькими уровнями вложенностями в "плоский" список:
iterable = [(1, 2), ([3, 4], [[5], [6]])]А если в плоский список нам нужно вытащить только элементы с первым уровнем вложенности?
list(more_itertools.collapse(iterable)) #[1, 2, 3, 4, 5, 6]
list(more_itertools.collapse(iterable, levels=1)) # [1, 2, [3, 4], [[5], [6]]]А вот так мы можем посмотреть, все ли элементы в коллекции уникальные:
more_itertools.all_unique([1, 2, 3, 4]) # TrueБиблиотека решает очень много типовых проблем, поэтому если научиться ей пользоваться - она сэкономит очень много времени. Возможно, я еще буду писать какие-то рецепты с ней, но это не точно 🌚...
more_itertools.all_unique([1, 2, 1, 4]) # False
PyPI | Документация
#more_itertools #itertools #библиотека #рецепт
PyPI
more-itertools
More routines for operating on iterables, beyond itertools
Как раз сегодня искал фреимворк для организации работы консьюмера RabbitMQ и на глаза попался Propan - декларативный фреимворк для работы с очередями сообщений.
Для чего это нужно? На базе очередей можно построить асинхронную коммуникацию сервисов, а это привет микросервисной архитектуре!
Для сравнения, вот столько кода нам нужно написать, чтобы сделать консьюмер при помощи aio_pika:
Что ещё умеет?
1) Кастить типы сообщений в модельки при помощи Pydantic.
2) Умеет работать с зависимостями (привет DI)
3) Имеет CLI утилитку, которая поможет сгенерировать проект, запустить несколько процессов воркеров, запустить хот-релоад для разработки.
4) А ещё есть огромное количество примеров, как им пользоваться.
5) Бонус - похоже у автора в планах прикрутить AsyncAPI (это как OpenAPI, только для очередей).
На данный момент стабильно работает с RabbitMQ, Redis и Nats. Kafka и SQS в бете, а NatsJs, MQTT, Redis Streams и Pulsar в планах.
Ну и накиньте звёзд автору, выглядит как то, что в будущем выстрелит :)
Github | Документация
#библиотека #propan
Для чего это нужно? На базе очередей можно построить асинхронную коммуникацию сервисов, а это привет микросервисной архитектуре!
Для сравнения, вот столько кода нам нужно написать, чтобы сделать консьюмер при помощи aio_pika:
import asyncioА вот столько с Propan:
import aio_pika
async def main():
connection = await aio_pika.connect_robust(
"amqp://guest:guest@127.0.0.1/"
)
queue_name = "test_queue"
async with connection:
channel = await connection.channel()
queue = await channel.declare_queue(queue_name)
async with queue.iterator() as queue_iter:
async for message in queue_iter:
async with message.process():
print(message.body)
asyncio.run(main())
from propan import PropanApp, RabbitBrokerЧистый кайф, не правда ли? Выглядит просто и понятно.
broker = RabbitBroker("amqp://guest:guest@localhost:5672/")
app = PropanApp(broker)
@broker.handle("test_queue")
async def base_handler(body):
print(body)
Что ещё умеет?
1) Кастить типы сообщений в модельки при помощи Pydantic.
2) Умеет работать с зависимостями (привет DI)
3) Имеет CLI утилитку, которая поможет сгенерировать проект, запустить несколько процессов воркеров, запустить хот-релоад для разработки.
4) А ещё есть огромное количество примеров, как им пользоваться.
5) Бонус - похоже у автора в планах прикрутить AsyncAPI (это как OpenAPI, только для очередей).
На данный момент стабильно работает с RabbitMQ, Redis и Nats. Kafka и SQS в бете, а NatsJs, MQTT, Redis Streams и Pulsar в планах.
Ну и накиньте звёзд автору, выглядит как то, что в будущем выстрелит :)
Github | Документация
#библиотека #propan
GitHub
GitHub - Lancetnik/Propan: Propan is a powerful and easy-to-use Python framework for building event-driven applications that interact…
Propan is a powerful and easy-to-use Python framework for building event-driven applications that interact with any MQ Broker - Lancetnik/Propan
Я знаю на опыте, как иногда не удобно работать с датами и временем в Питоне.
В стандартной библиотеке есть как и большое количество типов данных (date, time, tzinfo, timedelta, relativedelta, ...) так и куча модулей (datetime, time, calendar, dateutil, ...) которые помогают работать с ними. Но содержать в голове, да еще и уметь работать с ними иногда проблематично.
Одна из библиотек, которая облегчает работу с сабжем - arrow. Давайте покажу на практике, насколько с ней удобно работать:
1) Получение времени
Начнём с легкого - получим время по MSK и UTC:
Например, вот так можно заменить час в исходной дате:
Возьмем задачу. Нам нужно сказать, через сколько мы прибудем домой. Делается это так:
Как раз недавно решал следующую задачку: дается временной промежуток, для него нужно сделать почасовые интервалы. С arrow всё это делается вот так:
GitHub | Документация
#библиотека
В стандартной библиотеке есть как и большое количество типов данных (date, time, tzinfo, timedelta, relativedelta, ...) так и куча модулей (datetime, time, calendar, dateutil, ...) которые помогают работать с ними. Но содержать в голове, да еще и уметь работать с ними иногда проблематично.
Одна из библиотек, которая облегчает работу с сабжем - arrow. Давайте покажу на практике, насколько с ней удобно работать:
1) Получение времени
Начнём с легкого - получим время по MSK и UTC:
>>> arrow.now() # получаем текущее время по MSKА если нам надо текущее время с другой таимзоной?
2023-06-02T18:46:55.188882+03:00
>>> arrow.utcnow() # а теперь текущее, но в UTC
2023-06-02T15:46:55.188882+00:00
>>> arrow.get().to("US/Pacific")Это было просто. А что если нам нужно спарсить время из шаблона?
2023-06-02T08:49:39.794355-07:00
>>> arrow.get('2023/06-02|19:00:{00}', 'YYYY/MM-DD|HH:mm:{ss}') #обратите внимание на шаблон!Ещё arrow позволяет искать даты в строке по шаблону:
2023-06-02T19:00:00+00:00
>>>arrow.get('Он родился 16 Мая 2020 года', 'DD MMMM YYYY', locale="ru")2) Работа со временем
2020-05-16T00:00:00+00:00
Например, вот так можно заменить час в исходной дате:
>>>arrow.now().replace(hour=12)Или добавить к текущей дате +5 недель:
2023-06-02T12:00:29.180755+03:00
>>>arrow.now().shift(weeks=+5)Ну и в конце, давайте отформатируем текущее время в строку по шаблону:
2023-07-07T19:02:27.174819+03:00
>>>arrow.now().format('YYYY HH:mm:ss MM/DD')3) Очеловечивание времени
2023 19:05:04 06/02
Возьмем задачу. Нам нужно сказать, через сколько мы прибудем домой. Делается это так:
>>> start_time = arrow.now()А если мы хотим из человеческого формата перевести в машинночитаемый?
>>> arrive_time = start_time.shift(hours=+3, minutes=10, seconds=12)
>>> humanized = arrive_time.humanize(locale="ru", granularity=["hour", "minute", "second"])
>>> print(f"Вы прибудете домой {humanized}")
Вы прибудете домой через 3 часа 10 минут 12 секунд
>>> start_time.dehumanize(humanized, locale="ru")4) Работа с диапазонами
2023-06-02T22:28:08.141525+03:00
Как раз недавно решал следующую задачку: дается временной промежуток, для него нужно сделать почасовые интервалы. С arrow всё это делается вот так:
start = datetime(2023, 1, 1, 00, 00)В любом случае, рекомендую присмотреться, возможно для вас эта библиотека станет одним еще довольно удобным инструментом!
end = datetime(2023, 1, 1, 23, 00)
for r in arrow.Arrow.span_range('hour', start, end):
print(r)
(<Arrow [2023-01-01T00:00:00+00:00]>, <Arrow [2023-01-01T00:59:59.999999+00:00]>)
....
(<Arrow [2023-01-01T23:00:00+00:00]>, <Arrow [2023-01-01T23:59:59.999999+00:00]>)
GitHub | Документация
#библиотека
GitHub
GitHub - arrow-py/arrow: 🏹 Better dates & times for Python
🏹 Better dates & times for Python. Contribute to arrow-py/arrow development by creating an account on GitHub.
Если у вас есть желание понять как работает asyncio, threading или multiprocessing, либо же появились вопросы - рекомендую обратить внимание на superfastpython.com
Автор понятным языком рассказывает про доступные формы параллелизма и где какую можно применить. Для совсем начинающих он сделал "пути обучения", где раскиданы темы по каждой из форм.
Для особо искушенных автор продает книги по разным темам, но как по мне они в какой-то мере повторяют бесплатный материал, который уже есть на сайте.
Практически каждая статья сопровождается реальными примерами, которые можно самому запустить.
Все материалы на английском, но некоторые из них переводит на русский комьюнити (например цикл статей про asyncio).
#asyncio #threading #multiprocessing #статья
Автор понятным языком рассказывает про доступные формы параллелизма и где какую можно применить. Для совсем начинающих он сделал "пути обучения", где раскиданы темы по каждой из форм.
Для особо искушенных автор продает книги по разным темам, но как по мне они в какой-то мере повторяют бесплатный материал, который уже есть на сайте.
Практически каждая статья сопровождается реальными примерами, которые можно самому запустить.
Все материалы на английском, но некоторые из них переводит на русский комьюнити (например цикл статей про asyncio).
#asyncio #threading #multiprocessing #статья
Super Fast Python
Home - Super Fast Python
Master Python concurrency super fast. Learn threading, multiprocessing, and asyncio with step-by-step books and code tutorials.
Создание временных файлов
В процессе написания скрипта может потребоваться создание временных файлов, которые будут удалены автоматически после завершения работы скрипта или обработки файла.
Это может быть полезно по разным причинам - при обработке больших данных (которые не вместятся в буфер) или при проведении сложных операций (например, можно создать временный файл и натравить на него ffmpeg).
Для решения этих проблем в Python есть модуль
В процессе написания скрипта может потребоваться создание временных файлов, которые будут удалены автоматически после завершения работы скрипта или обработки файла.
Это может быть полезно по разным причинам - при обработке больших данных (которые не вместятся в буфер) или при проведении сложных операций (например, можно создать временный файл и натравить на него ffmpeg).
Для решения этих проблем в Python есть модуль
tempfile
. Нас интересует 2 функции - это TemporaryFile
и NamedTemporaryFile
.TemporaryFile
позволяет создать безымянный временный файл. Вот так можно создать временный текстовой файл, открыть его на запись и чтение (за это отвечает первый аргумент "w+t"
, подробнее можно прочитать здесь):from tempfile import TemporaryFile
with TemporaryFile("w+t") as t:
t.write("Hello, boxwithpython!")
t.seek(0)
data = t.read()
NamedTemporaryFile
используется для более продвинутых сценариев, так как он создает файл с именем, поэтому мы можем получить путь к нему и использовать его для дальнейших целей:from tempfile import
NamedTemporaryFile
with NamedTemporaryFile("w+t") as t:#std
t.write("Hello, boxwithpython!")
print(t.name) # /tmp/tmpljhsktjt
Shielded execution в asyncio
Допустим, есть следующий обработчик, который производит оплату:
Поможет нам в этом
Допустим, есть следующий обработчик, который производит оплату:
async def handler(request):Если соединение отвалится то обработчик упадет с ошибкой, так как серверу будет некуда отправлять ответ. Задача должна отмениться, но что если мы хотим, чтобы она выполнилась наверняка?
await service.pay(request.user)
return web.Response(text="payed")
Поможет нам в этом
asycio.shield()
. Он защищает задачу от отмены, даже в случае возникновения ошибки. Выглядит это следующим образом:async def handler(request):#asyncio #std
await asyncio.shield(service.pay(request.user))
return web.Response(text="payed")
Про __slots__
Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам и дополнительные расходы памяти.
Такое поведение нужно не всегда. Бывают случаи, когда мы точно знаем, какие атрибуты будут у наших экземпляров классов. Или же мы хотим ограничить добавление новых атрибутов. Именно для этого и существует
Слоты задаются через атрибут
В свою очередь, память экономится из-за того, что у класса не создается
#std #slots
Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам и дополнительные расходы памяти.
Такое поведение нужно не всегда. Бывают случаи, когда мы точно знаем, какие атрибуты будут у наших экземпляров классов. Или же мы хотим ограничить добавление новых атрибутов. Именно для этого и существует
__slots__
.Слоты задаются через атрибут
__slots__
в классе:class SlotsClass:Теперь мы не можем добавлять новые атрибуты к нашим объектам. Скорость доступа к атрибутам повышается на 25-30%, потому что при доступе к ним их больше не надо вычислять.
slots = ('foo', 'bar')
>>> obj = SlotsClass()
>>> obj.foo = 5
>>> obj.foo
# 5
>>> obj.another_attribute = 'test'
Traceback (most recent call last):
File "python", line 5, in <module>
AttributeError: 'SlotsClass' object has no attribute 'another_attribute'
В свою очередь, память экономится из-за того, что у класса не создается
__dict__
, который как раз хранил атрибуты.#std #slots
Коробка с питоном
Про __slots__ Python, аналогично другим динамическим языкам, таким как JavaScript, предоставляет возможность манипулирования объектами в рантайме, в том числе позволяет добавлять, изменять и удалять атрибуты. Цена этого – понижение скорости доступа к атрибутам…
__slots__ и наследование
Важно помнить, что при попытке унаследовать класс с
Из-за этого возникает неоднозначность, какой именно слот использовать в результирующем классе.
#std #slots
Важно помнить, что при попытке унаследовать класс с
__slots__
подкласс их унаследует, но так же и создаст __dict__
для новых атрибутов:class SlotsClass:Это стандартное и понятное поведение. Чтобы избежать создания
__slots__ = ('foo', 'bar')
class ChildSlotsClass(SlotsClass):
pass
>>> obj = ChildSlotsClass()
>>> obj.__slots__
# ('foo', 'bar')
>>> obj.foo = 5
>>> obj.test = 3
>>> obj.__dict__
# {'test': 3}
__dict__
, можно снова переопределить __slots__
в подклассе:class SlotsClass:А что с множественным наследованием?
__slots__ = ('foo', 'bar')
class ChildSlotsClass(SlotsClass):
__slots__ = ('baz',)
>>> obj = ChildSlotsClass()
>>> obj.foo = 5
>>> obj.baz = 6
>>> obj.something_new = 3
AttributeError: 'ChildSlotsClass' object has no attribute 'something_new'
class ClassA:Оно не работает. Потому-что каждый класс может иметь свои собственные
__slots__ = ('foo', 'bar',)
class ClassB:
__slots__ = ('baz',)
class C(ClassA, ClassB):
pass
TypeError: multiple bases have instance lay-out conflict
__slots__
, которые могут пересекаться с другими классами, а это может привести к тому, что объекты могут быть созданы неправильно или будут иметь непредсказуемое поведение. Из-за этого возникает неоднозначность, какой именно слот использовать в результирующем классе.
#std #slots
Интересный доклад, в котором разработчик из Uchi.ru рассказывает, как они строили realtime аналитику. Внутри доклада Kafka, Redis, Postgres и, внимание, Django.
#посмотреть #django
#посмотреть #django
YouTube
Real-time аналитика в Uchi.ru - как смотреть сложные метрики здесь и сейчас
Подписывайтесь на наш канал здесь и в телеграмм https://t.me/meetups_evrone, чтобы быть в курсе будущих митапов и не пропускать полезные доклады!
Андрей Скиба / Uchi.ru
В компании Uchi.ru с ростом ее размера возникла необходимость в наблюдении за ключевыми…
Андрей Скиба / Uchi.ru
В компании Uchi.ru с ростом ее размера возникла необходимость в наблюдении за ключевыми…
А я к вам с новостями.
FastAPI в версии
#fastapi #pydantic
FastAPI в версии
0.100.0-beta1
поддерживает Pydantic v2 в бета-режиме. Да-да, это тот самый Pydantic, внутренности которого написаны на Rust. Гайд по миграции можно почитать здесь, а релиз тут.#fastapi #pydantic
GitHub
Release 0.100.0-beta1 · tiangolo/fastapi
Install with:
pip install --pre --upgrade fastapi pydantic
Features
✨ Beta support for Pydantic version 2 ✨
The internals of Pydantic v2 were rewritten in Rust and it's currently available in ...
pip install --pre --upgrade fastapi pydantic
Features
✨ Beta support for Pydantic version 2 ✨
The internals of Pydantic v2 were rewritten in Rust and it's currently available in ...
Зачем нужно делать кастомную базовую Pydantic модель?
Сейчас некоторые со мной не согласятся, но я часто рекомендую делать базовую
Наличие такой глобальной модели позволяет настраивать поведение всех моделей в приложении. Рассмотрю несколько кейсов, когда это может понадобится.
1) Контроль над входными данными.
Например, мы хотим округлять все поля которые называются
P.S.: В коментах подметили, что такой подход неявный, и я с этим согласен. Ничего не мешает для таких целей сделать ещё одну базовую модель (
2) Кастомый энкодер/декодер json.
Пакет
Это происходит из-за того, что операций по (дe)сериализации json в приложении может быть много, и смена библиотеки, в этом случае, дает ощутимый прирост к общей скорости.
В pydantic есть 2 опции в конфиге, которые позволяют изменять поведение энкодера и декодера. Выглядит это так:
Сейчас некоторые со мной не согласятся, но я часто рекомендую делать базовую
pydantic
модель и наследовать все модели от неё.Наличие такой глобальной модели позволяет настраивать поведение всех моделей в приложении. Рассмотрю несколько кейсов, когда это может понадобится.
1) Контроль над входными данными.
Например, мы хотим округлять все поля которые называются
price
до трех знаков после запятой. Сделать это можно так:class CustomBaseModel(BaseModel):Валидаторы дают возможность изменять входящие данные, но это стоит использовать с осторожностью.
@root_validator()
def round_price(cls, data: dict) -> dict:
prices = {k: round(v, 3) for k, v in data.items() if k == "price"}
return {**data, **prices}
P.S.: В коментах подметили, что такой подход неявный, и я с этим согласен. Ничего не мешает для таких целей сделать ещё одну базовую модель (
PriceRoundBaseModel
), наследуясь от нашей базовой СustomBaseModel
и использовать её там, где такое поведение необходимо.2) Кастомый энкодер/декодер json.
Пакет
json
из стандартной библиотеки очень медленный. При необходимости ускорить сервис этот пакет в первую очередь пытаются заменить на что-то побыстрее.Это происходит из-за того, что операций по (дe)сериализации json в приложении может быть много, и смена библиотеки, в этом случае, дает ощутимый прирост к общей скорости.
В pydantic есть 2 опции в конфиге, которые позволяют изменять поведение энкодера и декодера. Выглядит это так:
def orjson_dumps(v, *, default):#pydantic
# orjson.dumps возвращает байты, поэтому нам надо их декодить, чтобы соответствовать сигнатуре json.dumps
return orjson.dumps(v, default=default).decode()
class CustomBaseModel(BaseModel):
class Config:
json_loads = orjson.loads
json_dumps = orjson_dumps
Коробка с питоном
Как раз сегодня искал фреимворк для организации работы консьюмера RabbitMQ и на глаза попался Propan - декларативный фреимворк для работы с очередями сообщений. Для чего это нужно? На базе очередей можно построить асинхронную коммуникацию сервисов, а это…
Автор Propan на Habr'е написал статью "Messaging для чайников. Утилизируем все возможности RabbitMQ на Python", где объясняет основы работы с этим брокером сообщений при помощи своего фреймворка.
Для начинающих - самое то.
#habr #propan #статья
Для начинающих - самое то.
#habr #propan #статья