Коробка с питоном
538 subscribers
45 photos
119 links
Заметки от Python-разработчика: сниппеты, обзоры пакетов, новости и другая полезная информация.

Автор: @kiriharu
Download Telegram
Гуглил как мне в Pydantic изменять входящие данные (забыл что это можно делать через валидатор) и натолкнулся на Blue Book - персональную вики одного разработчика, который пишет о различных темах - активизме, здоровье и так далее. Сейчас это обзывают модным словом Digital garden.

Но так как у нас канал о Python, и мы будем говорить о нём, у него есть небольшой раздел связанный с Python, со сниппетами, обсуждением библиотек и концепций.
Очень рекомендую заглянуть!

#ссылочки
Если вы любите делать игры на Godot или хотите начать их делать, при этом разбираться с GDScript вам лень - вы можете попробовать использовать godot-python. Godot - это прекраснейший движок, чтобы начать разрабатывать игры. Как по мне выглядит получше Unity, особенно для всяких небольших мобильных игр (особенно на фоне последних новостей).

Ребята пытаются добавить поддержку скриптинга на Python в Godot, и судя по тому как развивается проект - получается это у них довольно неплохо.

Из минусов я бы выделил: странный механизм экспорта проекта, вместе с игрой вы экспортите интерпретатор питона (хотя для кого-то это не минус), отсутствие pip из под коробки - его ставить нужно отдельно.

#библиотека #gamedev
Тут сборки PyPy для Python 3.8 и 3.9 зарелизили под маковский M1 (а если быть точнее - macOS ARM64). Отличная новость для разработчиков которые пользуются новыми макбуками!

#новости
Django 4.1 вышел! Список изменений, из интересного:

1) Асинхронный интерфейс к ORM. Под капотом выполняет sync_to_async(), про лимиты и подводные камни можно почитать тут. Сделали как и предполагал, чтобы сделать асинхронный get нужно добавить букву a в начало - aget, afirst и т.д.
2) В CBV можно делать асинхронные методы. Подробнее тут.
3) То, чего мне не хватало - ограничения Check, unique и exclusion проверяются во время валидации модели.
4) Добавили команду optimizemigration которая попытается оптимизировать миграции.

#новости #django
Давно как-то искал как можно поднять свой pypi сервер, в итоге остановился на комплексном решении от Sonatype Nexus. На testdriven.io вышла замечательная статья, как это сделать при помощи pypiserver. Всё разворачивается в нашем с вами любимом докере :)

Github сервера

#статья
Нашел свежий доклад со сравнением использования CTE и SFW в Django ORM и SQLAlchemy, спойлер: джанга в такие штуки не умеет, пишите raw sql.

Ещё внезапно для себя узнал насколько алхимия мощная в плане рефлексии и как с ней легко работать используя уже существующую базу.

#посмотреть #sqlalchemy #django
Нашел прекрасную утилиту которая позволяет модернизировать питонячий код - refurb.

Есть у нас вот такой код:
for filename in ["file1.txt", "file2.txt"]:
with open(filename) as f:
contents = f.read()

lines = contents.splitlines()

for line in lines:
if not line or line.startswith("# ") or line.startswith("// "):
continue

for word in line.split():
print(f"[{word}]", end="")

print("")

Refurb подсветит моменты, где код можно улучшить. Например, использовать кортежи вместо листов или выбрать более удобный метод для какой либо операции.

$ refurb main.py
main.py:3:17 [FURB109]: Use `in (x, y, z)` instead of `in [x, y, z]`
main.py:4:5 [FURB101]: Use `y = Path(x).read_text()` instead of `with open(x, ...) as f: y = f.read()`
main.py:10:40 [FURB102]: Replace `x.startswith(y) or x.startswith(z)` with `x.startswith((y, z))`
main.py:16:9 [FURB105]: Use `print() instead of `print("")`

Refurb не является тайп- или стаилчекером. Его задача - делать код лучше и не более того.

#утилиты
СТО из ДомКлик разобрал в своём докладе такую, казалось бы, банальную вещь как обработка ошибок. Доклад очень интересен структурно - в нём рассмотрены как и сами конструкции (ну а в друг кто забыл?), так и байткод создания исключений, рассмотрено их поведение на примере исходников интерпретатора.

А дальше - глубже! Автор так же затронул потоки, асинхронщину и неявные raise (например как с ZeroDivisionError).

#посмотреть #exceptions
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :)
Сам модуль itertools это набор из эффективных и быстрых по памяти инструментов, возвращающие итераторы. Сами по себе итераторы можно комбинировать с такими функциями как map(), list() лямбдами или же использовать их с помощью цикла for.

А сегодня мы начнем c самых простых - c бесконечных итераторов. Всего по документации их три: count(), cycle() и repeat().
Ниже я буду указывать аргументы по умолчанию и возможные передаваемые типы как тайп-хинты. Надеюсь ни у кого не возникнет проблем с пониманием.

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
count(start: int | float = 0, step: int | float = 1) -> Iterator[int | float]

Создает итератор, который возвращает равномерно распределенные значения, начиная с числа, указанного в аргументе start. Само значение-шаг указывается в переменной step:

>>> first = count(10, 2)
>>> next(first)
10
>>> next(first)
12
>>> next(first)
14
>>> second = count(0.1, 0.1)
>>> next(second)
0.1
>>> next(second)
0.2
>>> next(second)
0.30000000000000004
>>> next(second)
0.4

Небольшая загадка для вас - почему вместо 0.3 получили такое число? Пишите в комменты :)

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
cycle(p: Iterable) -> Iterator

Создает итератор, который возвращает элементы из переданного итератора поштучно и сохраняет их. После того как элементы закончились, он начинает возвращать сохранённые элементы и так до тех пор, пока мы сами не прервем его работу:

>>> first = cycle([1, 2])
>>> next(first)
1
>>> next(first)
2
>>> next(first)
1
>>> next(first)
2

Например, вот так при помощи функций islice и cycle мы можем сгенерировать список из 20 элементов из необходимой нам коллекции:

>>> list(islice(cycle([1, 2, 3, 4]), None, 20))
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]

#itertools #std
Коробка с питоном
Я заметил, что редко использую функции из itertools, в основном когда нужно произвести какие-то "красивые" шаманства. Теперь будем вспоминать саму библиотеку вместе, естественно с примерами :) Сам модуль itertools это набор из эффективных и быстрых по памяти…
repeat(object: T, times: Optional[int] = None) -> Iterable[T]

Создает итератор который возвращает объект снова и снова. Можно указать параметр times, который вернет объект заданное количество раз и завершит работу итератора:

>>> first = repeat("hehe", 3)
>>> next(first)
'hehe'
>>> next(first)
'hehe'
>>> next(first)
'hehe'
>>> next(first)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Очень часто repeat используют чтобы по быстрому сгенерировать коллекцию из элементов:

>>> list(repeat(10, 10))
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

#itertools #std
Там у Python 3.11 сегодня релиз, пройдемся по изменениям. Разделю пост на 2 части, сначала расскажу про всё остальное, а потом про typing, который снова прокачали.

1) Теперь Питон быстрее на 10-60% чем предыдущая версия. Почему такой разброс и где что ускорилось можно почитать здесь.
2) Exception Groups и *except. За это отвечает PEP 654, теперь есть возможность возбуждать и обрабатывать группы исключений. Судя по PEP сделано в основном для асинхронщины (asyncio.gather), колбеков и операций с несколькими попытками.
3) Для Exception появилась возможность добавлять примечания (notes). Теперь можно после обработки исключения добавить какую-то дополнительную информацию. Почитать подробно можно в PEP 678
4) Многие ждали и дождались - теперь в стандартной библиотеке есть tomllib, который позволяет работать с форматом TOML. Мотивация - его использует куча инструментов (black, mypy, pytest и т.д.), при чем каждый либо использует свою наколеночную реализацию, либо какую-то другую библиотеку. А тут решили стандартизировать, теперь это PEP 680.
5) Трейсбеки снова улучшили, теперь интерпретатор умеет показывать где именно находится ошибка, а не писать строку, где она произошла. Посмотреть на то как это выглядит можно в PEP 657.
6) Объявлены устаревшими следующие модули: aifc, audioop, cgi (за что?) и cgitb, chunk, crypt, imghdr (пользуйтесь PIL), mailcap, msilib, nis, nntplib, ossaudiodev, pipes (давно есть subprocess), sndhdr, spwd, sunau, telnetlib (вот этого я точно не могу понять), uu, xdrlib. Их удалят в 3.13.

А самому почитать об изменениях можно здесь.
Коробка с питоном
Там у Python 3.11 сегодня релиз, пройдемся по изменениям. Разделю пост на 2 части, сначала расскажу про всё остальное, а потом про typing, который снова прокачали. 1) Теперь Питон быстрее на 10-60% чем предыдущая версия. Почему такой разброс и где что ускорилось…
Немного с запозданием, но поговорим про новые фичи в typing.

1) NotRequired и Required поля в TypedDict.
Есть такой специальный класс как TypedDict - он позволяет добавить тайпхинты к словарю.
Теперь для описания полей в нём добавили три вещи:
1. NotRequired - показывает, что данного поля может и не быть в словаре.
2. Required - показывает, что данное поле обязательно должно быть в словаре
3. Аргумент total - если его значение ложно, то все поля в этом классе будут отмечены как NotRequired. По умолчанию оно истинно.

Почитать подробнее о нём можно вот тут

2) Self.
Ликуйте, господа, мы дождались! Больше никаких костылей с TypeVar!
Аннотация Self показывает что мы хотим вернуть инстанс текущего класса. Выглядит это как-то так:

class MyLock:
def __enter__(self) -> Self:
self.lock()
return self

Простенько и лаконично. Вот PEP 673.

3) LiteralString.
Относительно этого тайпхинта строки бывают двух типов - литеральные строки и произвольные строки. К литеральным строкам относятся константы, к произвольным - всё остальное. Данный тип указывает, что строка должна быть первого типа, на остальное оно будет ругаться. Вот пример:

def run_query(sql: LiteralString) -> ...
...

def caller(
arbitrary_string: str,
query_string: LiteralString,
table_name: LiteralString,
) -> None:
run_query("SELECT * FROM students") # ok
run_query(query_string) # ok
run_query("SELECT * FROM " + table_name) # ok
run_query(arbitrary_string) # type checker error
run_query( # type checker error
f"SELECT * FROM students WHERE name = {arbitrary_string}"
)

Исходя из примера видно, что использоваться это будет во всяких функциях, которые выполняют, например, SQL запросы или CLI-утилитах не давая на уровне тайпчекера засунуть в функцию очередной rm -rf. А PEP 675 можно почитать здесь.

4) Data class transforms
Декоратор который декорирует класс, метакласс или функцию которые являются декоратором. Получившийся декоратор умеет показывать тайпчекеру что декорируемый объект имеет фичи как у датакласса. Причина создания такой штуки проста - нет стандартного пути обозначения, что какой-то объект имеет свойства, присущие датаклассу. Привет Pydantic! PEP 681 читать здесь.

5) Вариативные дженерики.
Есть такая штука как TypeVar - она позволяет создавать дженерик параметризированный одним типом. А если нам нужно параметризировать произвольное количество типов? Теперь появился TypeVarTuple, который решает эту проблему.

Зачем это нужно: мы все знаем, что всякие датасотонисты любят наш питон из-за обилия всяких инструментов по типу NumPy или Pandas. Вариативные дженерики позволяют параметризировать структуры, которые подобны массивам, что в свою очередь позволяет находить ошибки при операции с этими структурами тайпчекеру.
PEP 646 читать здесь.

#pep
О работе с PostgreSQL JSONB в SQLAlchemy

JSON поля в PostgreSQL - это действительно удобный способ хранения данных, наряду с реляционной моделью. Они могут понадобиться там, где нужно произвести денормализацию данных для ускорения работы или для хранения гетерогенных данных.

Алхимия поддерживает работу с JSON-полями нативно, поэтому мы можем добавлять эти поля в свои модельки. Но есть пара нюансов, связанных с тем, каким образом алхимия проверяет изменения в этих полях.

Допустим, есть у нас вот такой класс:

class Config(Base):
id = Column(Integer, primary_key=True)
config = Column(JSONB)
tablename = 'config'

А теперь попытаемся добавить запись и изменить её:

cfg = Config(
id=1,
config={'some_const': 1}
)

session.add(cfg)
session.commit()

cfg.config['test'] = 2

session.add(cfg)
session.commit()

# упадёт с assertion error
assert cfg.config['test'] == 2

Почему оно упадёт с assertion error?
Внимательно прочитаем документацию SQLAlchemy и найдем вот такой забавный пункт: The JSON type, when used with the SQLAlchemy ORM, does not detect in-place mutations to the structure. То есть, при изменения структуры нашего объекта Алхимия не понимает, произошло ли какое-то изменение или нет.
Есть 2 решения этой проблемы:

1) Использовать класс MutableDict, если у нас нет вложенности в JSONB объектах:
Просто оборачиваем этой штукой наш тип в модели и радуемся жизни. Выглядит это как-то так:
...
config = Column(MutableDict.as_mutable(JSONB))

2) Используем библиотеку sqlalchemy-json, если у нас есть вложенность:
В данном случае наше поле будет выглядить как-то так:
from sqlalchemy_json import mutable_json_type
...
config = Column(mutable_json_type(dbtype=JSONB, nested=True))

Ссылочки:
1) Adding mutability to json
2) Mutation tracking
3) Mutation tracking in nested JSON structures using SQLAlchemy

#sqlalchemy #рецепт
Ежегодный опрос питонистов от JetBrains начался!

Зачем он нужен: каждый год JetBrains делает опрос для понимания, что из себя представляет Python разработка сейчас - что люди на нём кодят, что используют и так далее.

Опрос занимает 10-15 минут, а для дополнительной мотивации джеты подарят 20 случайным респондентам 100$ Amazon Gift Card!

Пройти можно тут.
Обнаружил у одного Python core developer репозиторий с названием python-horror-show.
В репозитории куча взрывающих мозг примеров с объяснением, почему что-то работает именно так как показано. Один из забавных примеров называется "Enter the void":

>>> all([])
True
>>> all([[]])
False
>>> all([[[]]])
True


#ссылочки
Большое интервью Гвидо ван Россума час назад вышло у Лекса Фридмана

https://www.youtube.com/watch?v=-DVyjdw4t9I

0:00 - Introduction
0:48 - CPython
6:01 - Code readability
10:22 - Indentation
26:58 - Bugs
38:26 - Programming fads
53:37 - Speed of Python 3.11
1:18:31 - Type hinting
1:23:49 - mypy
1:29:05 - TypeScript vs JavaScript
1:45:05 - Best IDE for Python
1:55:05 - Parallelism
2:12:58 - Global Interpreter Lock (GIL)
2:22:36 - Python 4.0
2:34:53 - Machine learning
2:44:35 - Benevolent Dictator for Life (BDFL)
2:56:11 - Advice for beginners
3:02:43 - GitHub Copilot
3:06:10 - Future of Python

#IT #Python
Я всё чаще и чаще замечаю Rust рядом с Python...

Совершенно недавно я нашел вот такой HTTP сервер на Rust для приложений на Python - Granian.

Разработчики ставят перед собой следующие цели:
+ Поддержка HTTP версий 1, 2 и 3
+ Один пакет для всех платформ
+ Сервер должен заменить Gunicorn/Uvicorn
+ Обеспечение большей производительности по сравнению с конкурентами.

Сейчас оно умеет в:
+ ASGI/3 и RSGI
+ HTTP/1 и HTTP/2
+ Вебсокеты над HTTP/1 и HTTP/2
+ Поддержка SSL

К сожалению проект пока что в состоянии разработки, поддерживает Питон 3.7 и выше на Unix-платформах и 3.8 и выше на Windows.
Очень надеюсь что с этого что-то вырастет.

#библиотека