Python Заметки
2.22K subscribers
62 photos
2 videos
2 files
229 links
Интересные заметки и обучающие материалы по Python

Контакт: @paulwinex

⚠️ Рекламу на канале не делаю!⚠️

Хештеги для поиска:
#tricks
#libs
#pep
#basic
#regex
#qt
#django
#2to3
#source
#offtop
Download Telegram
Unofficial Windows Binaries for Python Extension Packages - известная страница с множеством скомпилированных python-библиотек для Windows. Её вёл Christoph Gohlke и любезно нам собирал whl пакеты. Очень часто эа страница помогала и мне и, вероятно, многим из вас.

В июне 2022 года из-за отсутствия финансирования проект был закрыт и обновления долго не выходили. Позже и страница была удалена😭

В начале 2023 года Christoph Gohlke создал репозитории на GitHub которые заменили этот "сервис". На его странице в самом верху можно найти несколько ссылок на эти репозитории.

В частности репозиторий Pymol-open-source wheels for Python on Windows. Не могу сказать что это уже полноценная замена, кажется новые библиотеки добавляются неспеша, но это уже что-то. Активность можете проследить самостоятельно.

А еще там есть эксперементальные сборки для ARM64.

PS. Если знаете где есть подобные архивы, поделитесь в коментах.

#libs
🔥3
Когда требуется быстро расшарить файлы в локальную сеть со своего компа можно использовать дефолтный python-сервер. Все решается одной командой.

python3 -m http.server


Но это бывает неудобным если нужно скачать папку или залить файлы. В этом случае более удобным будет быстрый FTP сервер.

Я себе сделал шорткат для поднятия простого FTP сервера без авторизации на базе библиотеки pyftpdlib.

Варианты запуска:

# на рандомном порту read only
python3 -m pyftpdlib

# на указанном порту
python3 -m pyftpdlib -p 22222

# с доступом на запись
python3 -m pyftpdlib -w

# с авторизацией
python3 -m pyftpdlib -w --user=name --password=123

# полный список аргументолв
python3 -m pyftpdlib -h


Мой алиас для расшаривания в текущей директории

alias ftp="python3 -m pyftpdlib -w -p 22222"


Теперь можно подключть FTP соединение как удалённую директорию стандартными средствами OS. В Windows это Add Network Location, в Linux - зависит от дистрибутива. Ищите в разделе Network вашего файлового браузера.
Также можно использовать сторонние клиенты, например FileZilla.

А здесь подробней про http.server

#libs #tricks
🔥12👏2👍1
А вот и наглядное использование контроля стилей через свойства виджета. Билиотека qt-material.

В этом разделе написано, что через свойство class можно контролировать цвет кнопки, по аналогии с css стилями.

btn_danger.setProperty('class', 'danger')
btn_warning.setProperty('class', 'warning')
btn_success.setProperty('class', 'success')


#libs #qt
👍3🔥2
JSON API сейчас весьма актуален в сфере веб-приложений.
Но у стандартной библиотеки json есть проблема - она относительно медленная.

Если ваше веб приложение должно тянуть держать нагрузку, то такие популярные операции как сериализация и десериализация JSON хорошо бы максимально оптимизировать. Каждый запрос это преобразование JSON-строки в объект, обработка запроса, и обратное кодирование объекта в JSON-строку. Буквально - каждый!

Исходные данные для теста:
file = 'data.json'  # 156kb
with open(file, 'r') as f:
file_data = f.read()
data = json.loads(file_data)
count = 50000

Сделаем эталонный замер стандартной библиотеки
start = time.perf_counter()
for i in range(count):
json.loads(file_data)
end = time.perf_counter()
json_time_decode = end - start

start = time.perf_counter()
for i in range(count):
json.dumps(data)
end = time.perf_counter()
json_time_encode = end - start

print(f'Json time: {round(json_time_decode, 2)}s/{round(json_time_encode)}s')
# Json time: 50.04s/40s

Итак, какие есть альтернативы?

Сравнивать будем с библиотеками orjson и ujson.
Код находится на github, а здесь приведу только результаты.
Json time: 50.04s/40s
Orjson time: 20.27s [2.47x speed] / 5.46s [7.35x speed]
Ujson time: 47.29s [1.06x speed] / 35.04s [1.15x speed]

Неплохой прирост с orjson я считаю! А вот ujson не особо опередил.

Тест проводился на Windows 10. Интересно, что на Linux ujson даже медленней чем стандартный. Но, думаю, это у меня какие-то проблемки.

В тесты я не добавил simplejson. Он не сильно быстрей стандартного модуля.

Если вы пишете приложения на FastAPI, то в 3 строки можете ускорить обработку JSON-ответа! Есть встроенный класс для этого.

1. Установка
poetry add orjson
# or
pip install orjson

2. Имопрт
from fastapi.responses import ORJSONResponse 
# Там же лежит и UJSONResponse

3. Подключение
app = FastAPI(
...
default_response_class=ORJSONResponse
)


Для Django тоже есть решение: drf-orjson-renderer

#libs
🔥64👍2
Pyrogram закрылся!

Популярная библиотека pyrogram больше не будет развиваться. 24-го декабря мейнтейнер перевёл репозиторий в архив и написал сообщение с разъяснениями.

Если вы используете pyrogram в своих проектах, то у меня для вас плохая новость. Когда в Telegram появятся новые функции или изменится API, ваш проект перестанет работать. Рекомендую оперативно его переписывать.

Актуальная альтернатива - Telethon
➡️ https://github.com/LonamiWebs/Telethon

Документация здесь
📖 https://docs.telethon.dev/en/stable/

#libs
😱3😢21
Использование Pydantic сегодня стало нормой, и это правильно. Но иногда на ревью вижу, что используют его не всегда корректно.
Например, метод BaseModel.model_dump() по умолчанию не преобразует стандартные типы, такие как datetime, UUID или Decimal, в простой сериализуемый для JSON вид. Тогда пишут кастмоный сериализатор для этих типов чтобы функция json.dump() не падала с ошибкой.

import uuid
from datetime import datetime
from decimal import Decimal
from uuid import UUID
from pydantic import BaseModel

class MyModel(BaseModel):
id: UUID
date: datetime
value: Decimal


obj = MyModel(
id=uuid.uuid4(),
date=datetime.now(),
value='1.23'
)
print(obj.model_dump())
# не подходит для json.dump
# {
# 'id': UUID('4f8c1bc4-25fd-40cd-9dbe-2c73639b0dc1'),
# 'date': datetime.datetime(2025, 12, 12, 12, 12, 12, 111111),
# 'value': Decimal('1.23')
# }
# добавляем свой кастомный сериализатор
json.dumps(obj.model_dump(), cls=MySerializer)
# {
# 'id': '4f8c1bc4-25fd-40cd-9dbe-2c73639b0dc1',
# 'date': '2025-12-12T12:12:12.111111',
# 'value': '1.23'
# }


В данном случае класс MySerializer обрабатывает datetime, UUID и Decimal. Например так:
class MySerializer(json.JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return str(o)
elif isinstance(o, datetime):
return o.isoformat()
elif isinstance(o, UUID):
return str(o)
return super().default(o)


Специально для тех, кто всё еще так делает - в этом нет необходимости!
Pydantic может это сделать сам, просто нужно добавить параметр mode="json".
json.dumps(obj.model_dump(mode="json"))
# {
# 'id': '4f8c1bc4-25fd-40cd-9dbe-2c73639b0dc1',
# 'date': '2012-12-12T12:12:12.111111',
# 'value': '1.23'
# }


#pydantic #libs
9👍7🔥5
В работе с медиа файлами часто требуется определить не просто расширение, а его, скажем так, "категорию". Тоесть определить это видео, аудио или картинка. Примерно в 10 случаях из 10 в ревью я вижу обычный хардкодинг с большим мапингом и соответствующим поиском по нему.

file_type_by_ext = {
'video': ['.mp4', '.mov', '.mkv', ...],
'audio': ['.mp3', '.wav', '.ogg', ...],
'image': ['.jpg', '.png', '.exr', ...]
}


Для таких случаев есть простой способ - стандартная библиотека mimetypes.

import mimetypes
mimetypes.guess_type("example.txt")
# ('text/plain', None)

Причём ей не нужен файл, достаточно просто имени строкой.

Первый элемент кортежа это MIME-тип (Multipurpose Internet Mail Extensions Type) - стандартный способ идентификации формата файла.

Формат: type/subtype

type - общая категория данных (text, video, image)
subtype - конкретный формат внутри категории

mimetypes.guess_type("photo.jpg")
# ('image/jpeg', None)
mimetypes.guess_type("render.mp4")
# ('video/mp4', None)


Второй элемент это тип кодировки содержимого, обычно для контейнеров типа gz и аналогичных.
mimetypes.guess_type("file.tar.gz")
# ('application/x-tar', 'gzip')
mimetypes.guess_type("backup.tar.bz2")
# ('application/x-tar', 'bzip2')


Итого, узнать категорию файла одной строкой:
mimetypes.guess_type('myfile.mov')[0].split('/')[0]
# video

Конечно при условии, что тип будет распознан, иначе будет None а не строка. Но об этом в следующий раз.

#libs #tricks
👍20
import mimetypes
mimetypes.guess_type("example.fbx")
# (None, None)

Формат не распознан, так как не зарегистрирован в системе.
Регистрация происходит с помощью функции mimetypes.init(). Эта функция автоматически вызывается при первом обращении.
Для каждой OS работает по-разному. В Windows читает реестр, в Linux достает всё из файла /etc/mime.types, в MacOS читает из системной БД.

На linux можно попробовать распознать тип через вызов
file --mime-type -b <filename>

эта команда попробует прочитать метадату самого файла, то есть должен быть доступ к файлу. Но это не гарантия успеха.

Можно попробовать использовать нестрогое соответствие IANA с помощью флага strict=False. Тогда будут учтены старые и нестандартные типы. Обычно они с префиксом x-

Новые типы можно добавлять самостоятельно.
mimetypes.add_type('application/x-fbx', '.fbx') # с точкой
mimetypes.guess_type("example.fbx")
# ('application/x-fbx', None)


Либо вызвать init() еще раз передав список текстовых файлов с нужными вам типами (без точки)
# my-mime-types.txt
application/x-fbx fbx
application/x-ogo ogo
application/x-aga aga

mimetypes.init(['my-mime-types.txt'])
mimetypes.guess_type("example.ogo")
# ('application/x-ogo', None)


Есть и обратная операция - получить расширение файла из mime-типа
mimetypes.guess_extension('image/jpeg')
# .jpg

Или все подходящие расширения
mimetypes.guess_all_extensions('image/jpeg')
# ['.jpg', '.jpe', '.jpeg', '.jfif']


Советую почитать полную документацию
Также обратите внимание на библиотеку content-types для работы с mime-типами, где больше возможностей.

#libs #tricks
🔥4
В Gunicorn добавили ASGI. Пока что beta.

gunicorn myapp:app --worker-class asgi


То есть теперь вместо "gunicorn воркеры + uvicorn ранеры" будет всё из одной библиотеки? Коненчо, если скорость не будет драматически ниже.

#libs
🔥3
Потоковая обработка часто встречается при работе с большими файлами или когда данные приходят частями. В Python есть множество инструментов для работы с такими данными. Самый известный - итератор файла по строкам. В веб-приложениях это стандарт для передачи файлов. Далее приведу несколько примеров.

Чтение файлов
with open('huge-file.txt') as file:
for line in file:
process_line(line)

Это позволяет нам читать текстовый файл по строкам не загружая всё в память.
Конечно, если позволяет формат данных. С JSON такое не сработает (ijson может в этом помочь).

Запись файла чанками
with open('file-to-save.txt',
'w') as file:
for line in iter_data():
file.write(line)


Частные случаи есть в разных библиотеках. Например DictWriter и DictReader из модуля csv позволяет работать с конкретным форматом данных а не просто текст.
import csv

with open('data.csv', 'r') as file:
reader = csv.DictReader(file)
for row in reader:
print(row)

with open('data.csv', 'a',
newline='') as f:
writer = csv.DictWriter(f,
fieldnames=['col1', 'col2']
)
for row in iter_objects():
writer.writerow(row)


Отдельно интересен ZipFile, позволяющий "открыть" файл сразу внутри архива и записывать его частями
import zipfile as zf

with zf.ZipFile(
'archive.zip',
'w',
compression=zf.ZIP_DEFLATED) as zf:
with zf.open(
'large_data.bin',
mode='w') as in_file:
with open(
'large_data.bin',
'rb') as source:
for chunk in iter(
lambda: source.read(1024),
b''):
in_file.write(chunk)


Создание хеша для большого файла
import hashlib

sha256 = hashlib.sha256()
with open(
'large-file.bin',
'rb') as f:
for block in iter(
lambda: f.read(1024), b''
):
sha256.update(block)
hash_sum = sha256.hexdigest()


Сжатие данных в файл отдельными чанками
import gzip

with gzip.open('data.gz', 'wb') as f:
for bin_chunk in iter_bin_data():
f.write(bin_chunk)


Чтение с записью в файл
with gzip.open('data.gz', 'rb') as f_in:
with open(
'extracted_data.txt',
'wb') as f_out:
for chunk in iter(
lambda: f_in.read(1024),
b''):
f_out.write(chunk)


Подсчет объектов из стрима. Добавление обновляет счетчики.
from collections import Counter

c = Counter()
for data in iter_objects():
c.update(data)


Это не все доступные примеры, их еще много. Каждый из них позволяет обрабатывать данные из потока не ожидая весь набор и не загружая их в оперативку.
Это очень полезная техника, которую я призываю использовать по назначению!

#tricks #libs
👍12
Стандартная библиотека asyncio это стандарт (начиная с Py3.4) для работы с асинхронным кодом. Но эта библиотека достаточно низкоуровневая, со своими проблемами, устаревшими подходами.
Чтобы исправить это, были созданы разные обертки и альтернативы с реализацией популярных инструментов и паттернов асинхронного программирования. Это такие библиотеки как:

- trio: улучшает корректность выполнения, не оставляя потерянных корутин при ошибках, то есть предлагает Structured Concurrency из коробки.

- curio: упрощение синтаксиса и читаемости кода, больше похоже на работу с потоками.

- anyio: универсальная обертка над asyncio или trio плюс множество вспомогательных инструментов.

anyio используется в FastAPI как основная библиотека для работы с асинхронным кодом и вызовом синхронного кода из асинхронного.


В общем, рекомендую почитать про возможности anyio, возможно вы более не будете использовать чистый asyncio в своих проектах)

Это совсем не значит что дефолтный asyncio плох, он тоже даёт достаточный для работы функционал и продолжает развиваться. Например, в версии 3.11 появились TaskGroup, с похожим на trio функционалом. Так что он тоже актуален, просто придется больше написать кода самостоятельно.

#libs #async
5👍2
Nuitka 4.0

Библиотека для компиляции python-кода в исполняемые файлы получила мажорный апдейт.

Ключевые изменения:
- ускорение сборки бинарника в 15 раз
- экспериментальная поддержка компилятора Zig
- возможность выбранные функции оставлять как есть, в виде байт кода через декоратор @nuitka_ignore
- бинарники работают до 30% быстрей (CPU-bound задачи)
- теперь можно вместо отдельного скрипта сборки использовать pyproject.toml. Весь конфиг в одном файле!
- улучшен контроль подключения DLL библиотек для Windows
- улучшена совместимость с рядом популярных и не очень пакетами

И в целом выбран путь на повышение производительности бинарной сборки.

Полная информация ➡️ здесь.

#libs
9👍2👏1😱1
В Python 3.6 был полностью переработан стандартный dict. Вместо разреженной таблицы данные стали храниться в плотных массивах. Это дало буст к скорости и экономию памяти. И, как сайд эффект, ключи стали упорядочены. В каком порядке добавляешь ключи, в таком можно и забрать.

Но при этом OrderedDict никуда не делся. Это сделано для совместимости?

Нет, между dict и OrderedDict всё ещё большая разница.

▫️ При сравнении в обычном dict проверяется только наличие ключа, а в OrderedDict проверяется их порядок
▫️OrderedDict основан на связном списке и имеет метод move_to_end() для изменения порядка элементов.
А метод popitem() позволяет удалять элемент как с конца так и из начала.
▫️OrderedDict это кастомный класс. Он не так оптимизирован как обычный dict. Работает дольше, места занимает больше.

В версии 3.7 он был переписан на С и стал быстрей, но всё еще уступает обычному dict.

Немного тестов:

Память: в 2.5-3 раза больше
Создание: в ~2 раза дольше
Удаление: в ~3 раза быстрей (popitem против del)
Поиск по ключу: примерно одинаково (хеш таблицы)

Код тестов↗️

Если вы используете OrderedDict, то это предполагает, что порядок ключей важен для логики программы.


#libs
5👍5