Покажу PEP 723+uv: зависимости в комментариях, раннер сам ставит и запускает. Примеры, плюсы и грабли, отдельно — про риски безопасности. Без README-плясок.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Tenacity — гибкая библиотека для ретраев (повторных попыток) с поддержкой экспоненциального бэкоффа, фильтрации по исключениям/результатам и асинхронных функций. Полезна для сетевых запросов, нестабильных API и flaky-тестов.
🟢 Декларативные ретраи через декоратор @retry🟢 Стратегии ожидания: фиксированная, экспоненциальная, джиттер, лимиты🟢 Условия повтора по типам исключений или значениям результата🟢 Хуки before/after и поддержка async def
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import requests
@retry(
stop=stop_after_attempt(5), # максимум 5 попыток
wait=wait_exponential(multiplier=0.5, max=8), # экспоненциальный бэкофф
retry=retry_if_exception_type((
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
)),
reraise=True # пробросить последнее исключение
)
def fetch_json(url: str) -> dict:
resp = requests.get(url, timeout=2)
resp.raise_for_status()
return resp.json()
data = fetch_json("https://httpbin.org/json")
print(data["slideshow"]["title"])
pip install tenacity
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Ресурс охватывает настройку окружений (venv/pyenv), управление зависимостями, упаковку и публикацию пакетов, тестирование, логирование, стиль кода и структуру проектов. Полезен как чеклист “best practices” для ежедневной разработки.
Материал подан структурировано с примерами и рекомендациями по инструментам, что помогает быстро выбрать правильный подход и стек под задачу.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Опишу, как в воскресенье API-гейтвей устроил бой с памятью, а простой скрипт вытащил прод: что случилось, как сработало, код, графики, постмортем и чек-лист.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
singledispatch позволяет объявить одну «общую» функцию и регистрировать для неё разные реализации в зависимости от типа первого аргумента. Удобно для сериализации, валидации и преобразований без длинных if isinstance(...).from functools import singledispatch
@singledispatch
def serialize(obj):
return str(obj) # поведение по умолчанию
@serialize.register
def _(obj: int):
return f"int:{obj}"
@serialize.register
def _(obj: list):
return f"list[{len(obj)}]"
print(serialize(10)) # ➔ int:10
print(serialize([1, 2, 3]))# ➔ list[3]
print(serialize(3.14)) # ➔ 3.14 (попадает в базовую реализацию)
@singledispatch выбирает реализацию по типу первого аргумента с учётом MRO (находит ближайший подходящий тип)..register(Type) добавляет реализацию для конкретного типа (и её унаследуют подклассы).functools.singledispatchmethod (класс-ориентированная перегрузка).Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
csv — встроенный модуль для чтения и записи табличных данных. Подходит для обмена данными с Excel/Google Sheets, логов и простых отчётов.import csv
from pathlib import Path
# Создадим входной CSV с заголовками
src = Path("employees.csv")
with src.open("w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "role", "salary"])
writer.writeheader()
writer.writerows([
{"name": "Alice", "role": "Engineer", "salary": "120000"},
{"name": "Bob", "role": "Manager", "salary": "135000"},
{"name": "Cara", "role": "Engineer", "salary": "125000"},
])
# Читаем и фильтруем инженеров с зарплатой > 120k
rows = []
with src.open("r", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
row["salary"] = int(row["salary"]) # приведение типов
if row["role"] == "Engineer" and row["salary"] > 120000:
rows.append(row)
# Сохраняем результат в новый CSV
dst = Path("top_engineers.csv")
with dst.open("w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "role", "salary"])
writer.writeheader()
writer.writerows(rows)
print(f"Сохранено: {dst} ({len(rows)} записей)")
csv.DictReader/DictWriter работает со словарями по именам колонок — удобнее, чем позиционные индексы.newline="" при открытии файла важно для корректных переводов строк на Windows.int для чисел).delimiter=";", quotechar='"', quoting=csv.QUOTE_MINIMALPlease open Telegram to view this post
VIEW IN TELEGRAM
В статье результаты тестирования pandas, openpyxl, Tablib, DuckDB, LibreOffice и даже связки с Rust. Кто справился лучше всех и как за 4 секунды Python «проглотил» полмиллиона строк.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🤯1
Зачем нужен __slots__ в Python и какие у него подводные камни?
Пример
class Point:
__slots__ = ("x", "y") # фиксируем набор атрибутов
def __init__(self, x, y):
self.x, self.y = x, y
p = Point(1, 2)
print(hasattr(p, "__dict__")) # ➔ False (нет словаря экземпляра)
p.z = 3 # ➔ AttributeError: 'Point' object has no attribute 'z'
Ответ
__slots__ отключает создание __dict__ у экземпляров и фиксирует набор допустимых атрибутов. Это сокращает память (особенно важно для миллионов объектов) и может ускорять доступ к полям. Обратная сторона — нельзя динамически добавлять новые атрибуты, а некоторые механики требуют дополнительных слотов.
🟢 Запомнить:
Используйте __slots__, когда класс создаёт много экземпляров с стабильным набором полей.
Для поддержки weakref добавьте '__weakref__' в __slots__.
В иерархиях все классы должны быть «кооперативны»: у подклассов тоже задавайте __slots__ (или потеряете выгоды/совместимость).
В dataclasses пишите@dataclass (slots=True) (Python 3.10+) вместо ручного __slots__.
Если нужен словарь атрибутов, добавьте '__dict__' в __slots__ — но тогда теряете экономию памяти.
Please open Telegram to view this post
VIEW IN TELEGRAM
glom — декларативная библиотека для извлечения и трансформации вложенных структур данных (dict/list/tuple). Позволяет описывать «что достать и как преобразовать» короткой спецификацией вместо ручных циклов и
if.🟢 Доступ к глубоко вложенным полям без try/except🟢 Маппинг и агрегирование списков одной строкой🟢 Значения по умолчанию и безопасные обходы отсутствующих ключей🟢 Композиция спецификаций для сложных преобразований
from glom import glom, T
data = {
"user": {"name": "Alice", "age": 30, "emails": ["a@x.io", "b@y.io"]},
"orders": [{"id": 1, "total": 99.9}, {"id": 2, "total": 15.0}]
}
spec = {
"username": T["user"]["name"],
"primary_email": T["user"]["emails"][0],
"order_ids": (T["orders"], [T["id"]]),
"sum_total": (T["orders"], sum, [T["total"]]), # суммируем totals
}
print(glom(data, spec))
# ➔ {'username': 'Alice', 'primary_email': 'a@x.io', 'order_ids': [1, 2], 'sum_total': 114.9}
# Безопасный доступ с дефолтом:
print(glom(data, "user.phone", default="no phone")) # ➔ 'no phone'
pip install glom
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
Я расширил ChameleonLab: от картинок к документам. Разбираю стеганографию в DOCX/PDF, риски, методики и защиту; покажу демо и грабли. Да, ваши отчёты тоже могут шептать.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Python Tutor позволяет пошагово видеть, как исполняется ваш код: значения переменных, стек вызовов, переходы по строкам. Полезен для обучения, отладки рекурсий, понимания областей видимости и работы списков/словарей.
Примечательно, что сервис поддерживает пошаривание сессий и встроенные “сценарии” с примерами — удобно разбирать код с командой или наставником прямо по ссылке.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
cached_property превращает метод в свойство, которое вычисляется один раз при первом доступе и кэшируется в экземпляре. Полезно для дорогих вычислений и ленивой инициализацииfrom functools import cached_property
import time
class Report:
def init(self, user_id):
self.user_id = user_id
@cached_property
def stats(self):
print("Вычисляю...") # выполнится только при первом доступе
time.sleep(1)
return {"orders": 42, "spent": 199.99}
r = Report(123)
print(r.stats) # первый доступ — расчёт и кэширование
print(r.stats) # последующие — мгновенно из кэша
del r.dict["stats"] # инвалидация кэша
print(r.stats) # пересчёт после инвалидации
@cached_property сохраняет результат в instance.__dict__ под именем свойства.del obj.__dict__["attr"] — значение пересчитается при следующем доступе.Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
Покажу, как я собрал TANKOLINI NAPIERDOLKI: монохром, канвас и пиксели, тетрис-вайб и редактор карт; мультиплеер на Python, карты в PostgreSQL, комнаты в Redis. Мемы прилагаются.
Please open Telegram to view this post
VIEW IN TELEGRAM
👌1
TOML — удобный формат конфигураций. В Python 3.11+ есть встроенный
tomllib для чтения; для записи используем лёгкий tomli-w.# pip install tomli-w
import tomllib # Python 3.11+
import tomli_w # запись TOML
from pathlib import Path
cfg = Path("config.toml")
# Создадим дефолтный конфиг, если его нет
cfg.write_text("[app]\nname='Ghostly'\ndebug=true\n\n[db]\nhost='localhost'\nport=5432\n",
encoding="utf-8") if not cfg.exists() else None
# Чтение (важно: бинарный режим для tomllib)
with cfg.open("rb") as f:
conf = tomllib.load(f)
# Изменим настройки
conf["app"]["debug"] = False
conf["db"]["host"] = "db.prod.local"
conf["db"]["port"] = 6432
# Запись обратно в TOML
cfg.write_text(tomli_w.dumps(conf), encoding="utf-8")
tomllib.load(f) — чтение TOML (3.11+); для ≤3.10 используйте tomli.tomli_w.dumps(dict) — сериализация словаря в TOML.tomllib в бинарном режиме: open("rb").Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🙏1
В чём разница между __getattr__ и __getattribute__ в Python и почему легко поймать бесконечную рекурсию?
Пример
class Demo:
def __getattribute__(self, name):
if name == "x":
return 42
# ВАЖНО: делегировать наверх, иначе рекурсия
return super().__getattribute__(name)
def __getattr__(self, name):
# Вызывается ТОЛЬКО если обычный поиск атрибута провалился
return f"<missing:{name}>"
d = Demo()
print(d.x) # ➔ 42
print(d.y) # ➔ <missing:y>
class Bad:
def __getattribute__(self, name):
# Плохо: вызывает себя же через getattr → бесконечная рекурсия
return getattr(self, name)
# Bad().anything # ➔ RecursionError
Ответ
__getattribute__(self, name) вызывается всегда при доступе к атрибуту экземпляра. Если внутри него вы обращаетесь к атрибутам через self.…/getattr(self, …), вы снова попадаете в __getattribute__ → RecursionError. Правильно — звать super().__getattribute__(name) или напрямую object.__getattribute__(self, name).
__getattr__(self, name) вызывается только если атрибут не найден обычным путём (включая __getattribute__). Удобен для ленивых/виртуальных атрибутов и значений по умолчанию.
🟢 Запомнить:
Внутри __getattribute__ используйте object.__getattribute__(self, name)/super() для безопасного доступа.
Чтобы передать управление __getattr__, поднимайте AttributeError из __getattribute__.
Меняя протокол доступа, легко сломать дебаг/инспекцию; держите реализацию минимальной и предсказуемой.
Для динамики часто достаточно __getattr__; __getattribute__ — инструмент «тяжёлого класса».
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7