P(hD)ython
214 subscribers
39 photos
26 links
О Python, PhD, распределённых системах и не только

Автор - Михаил Масягин (@masyagin1998):
- Python Lead в NDA HFT;
- преподаватель в Бауманке;
- эксперт по СУБД System Design World;
- любитель PhD и авторегрессии.
Download Telegram
Channel photo updated
«Розыгрыш 🎰, дедлайны и Рождество 🎄»

Ну что, классика жанра: к дедлайну я, как всегда, не успел - поэтому розыгрыш будет не к Новому году, а… к Рождеству 😅

В честь праздников разыгрываю среди подписчиков бумажную книгу Майкла Льюиса «Flash Boys: A Wall Street Revolt» - самое то, чтобы начать плавное погружение в HFT, попивая какао ☕️ у рождественской ёлки 🎄

Если вы в Москве - с радостью вручу лично и угощу кофе/пивом/какао 🍾

Как участвовать:
- подписаться на канал (нас уже 150+ - спасибо вам ❤️);
- нажать кнопку «Участвую!» под постом;
- сделать репост в своё сообщество, добавив друзей по ссылке и т.д. - шанс на победу x2 💰;
- ждать итогов 7 января 🎁.

С уважением,
Михаил Масягин
134🎄3🥰21💯1🍓1
«Итоги 2025: Python 3.14 🐍 и немного моей жизни 🎄»

Ну что, под Новый Год самое время подвести итоги. 7 октября 2025 вышла финальная версия Python 3.14.0, меняющая как внутреннее устройство языка, так и добавляющая в него новые полезные функции. Число и значимость изменений сравнимы с 3.4.0, подарившей нам asyncio в 2014 году!

1. Kill GIL! Как известно, у змей - раздвоенный язык, и Python теперь - не исключение. Начиная с 3.14.0 существуют 2 сборки CPython - стандартная 3.14 и... 3.14t без GIL! Теперь, пусть и с рядом новых трудностей, мы можем достигать в Python истинного параллелизма потоков исполнения прямо как в C\C++, Java и т.д.!
2. concurrent.interpreters - до тех пор, пока 3.14t не станет стандартом запуск нескольких независимых интерпретаторов всё ещё актуален. Что же, теперь мы можем делать это в рамках одного процесса, минимизируя траты на IPC!
3. Отладка современного Python-кода стала ещё проще и приятнее - к целевому процессу можно подключиться на лету без необходимости его перезапуска!
4. Инкрементальный GC - осталось лишь 2 поколения объектов: молодое и старое, и на каждом цикле GC сканируется всё молодое и эвристически лишь часть старого. За счёт этого работа сборщика мусора стала предсказуемее и стабильнее.
5. Ряд оптимизаций байткода Python, включая Tail Calling.
6. t-strings - всё те же f-strings, но теперь завёрнутые в отдельный класс!
7. Аннотации больше не тормозят import'ы, так как вычисляются отложенно, а ещё их можно анализировать через annotationlib.
8. Все мы знаем, что zstd - это база, а теперь это знает и Python! zstandard теперь входит в стандартную поставку интерпретатора!
9. И много-много чего ещё!

Все фичи подробно разберём в постах 2026 года!

Ну и пару слов о моих итогах 😅:

1. Начал разбирать Кабанчика 🐷 на System Design World с @vova_dev!
2. Запустили с @vova_dev наш курс по System Design! Уже прошло 3 потока и на подходе 4!
3. + 1 новая статья ВАК/WoS и Conference Paper в IEEE - за последнее отдельное спасибо @Kvassir!
4. Выступил с лекциями по HFT на ICPC, FaangTalk (@volyx ❤️) и в Бауманке!
5. Пережил ремонт и даже несмотря на него побывал в новой стране - Вьетнаме!
6. Познакомился со столькими замечательными людьми: @vova_dev, @LooksOfTheMoon, @terapsyda - вы лучшие ❤️!
7. Начал активно собесить не только питонистов, но и растовчан на работе 💪!
8. Завёл канал, где вы сейчас и читаете этот пост! Нас уже 160+ 👫!

Были и факапы, главный - диссер с конца 2025 года переехал на весну 2026 года 😢.

К чему я это всё? Думаю, к тому, что надо работать, фигачить, выкладываться на 110%! Достичь всего - нереально, многого - очень сложно, но возможно 💪!

С Новым Годом, друзья 🎄! Соблюдаем Work-Job Balance и идём седлать огненного коня 2026 года 🎠!

С уважением,
Михаил Масягин
Please open Telegram to view this post
VIEW IN TELEGRAM
12🎉4💯3🎄21🎅1
P(hD)ython
🎉 Розыгрыш завершен! 🏆 Победители: 1. @ashm_tech 🔍 Проверить результаты
«Сказано - сделано 😎🚀

@ashm_tech получил свой приз 📕!

С уважением,
Михаил Масягин
🎉8👍2🤝2🥰1
«CQRS: нормально делай - нормально будет!»

Разбавим Python-посты архитектурой!
На днях со студентами System Design World обсуждали паттерны, и закономерно всплыл CQRS.
Его просто обожают на System Design Interview, и... регулярно путают с CQS!

Micro vs Macro

CQS (Command-Query Separation) - это принцип создания классов и API.

- Command-методы меняют состояние и либо НЕ отдают данные (void), либо возвращают служебные значения (id, ok, error и т.д.);
- Query-методы возвращают данные и никогда НЕ меняют состояние.

На простых классах от CQS мало пользы, зато при написании фабрик, репозиториев и прочих паттернов он реально выручает:
- меньше неявного поведения;
- проще кэшировать и оптимизировать;
- проще поддерживать и дебажить код.

Пример:
from dataclasses import dataclass

@dataclass(frozen=True)
class Car:
num: str

class Base:
def __init__(self):
self._cs = {}

class FactoryBad(Base):
# get with unexpected side effect
def get(self, num: str) -> Car:
if num not in self._cs:
self._cs[num] = Car(num=num)
return self._cs[num]

class FactoryCQS(Base):
def find(self, num: str) -> Car | None:
return self._cs.get(num)

def get(self, num: str) -> Car:
return self._cs[num]

def register(self, num: str) -> None:
if num in self._cs:
raise ValueError(f"Car with number '{num}' already exists!")
self._cs[num] = Car(num=num)


CQRS (Command Query Responsibility Segregation) - это архитектурный паттерн:
- разные пути и модели данных для записи и чтения - write-side и read-side;
- часто разные Handler'ы, контракты и схемы - но всё же это детали реализации.

По сути CQRS - это CQS «на стероидах».

CQRS - Кафка, Стриминг, 2 Сурса

Частая ошибка - воспринимать CQRS как обязательную связку из условных read- и write-СУБД, очереди и Eventual Consistency.
Действительно, так часто бывает, но в первую очередь CQRS - про разделение путей и моделей данных, а не про инфраструктуру.

Начать внедрение CQRS можно и с 1 СУБД:
- write - нормализованные таблицы под базовые сущности;
- read - денормализованные таблицы/view под чтение;
- если обновлять read-проекции синхронно с write-проекциями, можно получить и Strong Consistency.

Зачем всё это

CRUD-сервис «на всё» (create, update, get, find, ...) быстро «пухнет»:
- чтение и запись смешиваются, API становится неочевидным;
- хотим масштабировать чтение, но read-only инстансы вынужденно тащат write-зависимости и флаги/роутинг;
- репозиторий превращается в комбайн с бесконечными зависимостями;
- страдает производительность;
- сложнее растить команду.

CQRS предлагает решение этой проблемы:
- изолированные Handler'ы для команд и запросов (часто реально «по 1 файлу на операцию»);
- лишь нужные зависимости в каждом Handler'е - ускоряет разработку и реально отделяет write-side от read-side.

CQRS - не серебряная пуля:
- если проект компактный и несложный - лучше CRUD + нормальный репозиторий;
- CQRS добавляет бойлерплейт. Даже если код «генерится Claude'ом», растет объём и контекст.

Идемпотентность команд - must have

Команды могут повторяться из-за retry, timeout и at-least-once доставки. Handler должен быть идемпотентным и не допускать создания дубликатов.

Блеск CQRS

CQRS раскрывается, когда система становится read-heavy/нуждается в разных формах данных. Тогда вы:
- масштабируете read- и write-side независимо;
- держите read-модели под конкретные задачи: поиск, отчёты и т.д.

Отдельный плюс - несколько read-моделей одновременно: Postgres для запросов, Elastic для FTS и т.д. При этом они могут строить свои проекции из единого потока событий (event bus, outbox, CDC и т.д.). Отсюда и дружба с Event Sourcing: при хранении изменений как Event Log, проекции можно пересобирать с нуля.

Идеи CQS и CQRS во многом звучат как «нормально делай - нормально будет», они очень интуитивны. Тем не менее выработка общих терминологии и понимания - это всегда большой плюс.

В следующий раз разберём Event Sourcing!

С уважением,
Михаил Масягин

P.S. Рекомендую к просмотру выступление Андрея Цветциха.
🔥76🎉2
«Отель для настоящих HFT-разрабов и квантов 😎

С уважением,
Михаил Масягин

P.S. Кто угадает страну... тот молодец
🔥8😁4👏2
«Мам, сфоткай типо я кант-трейдор 🥴»

С уважением,
Михаил Масягин
🔥11👍7🥴1💯1
«Айтишники 💻 и металлурги 🛠»

Последние пару месяцев активно провожу собесы Python-разработчиков: отвечаю за алгоритмическую секцию, где кандидатам предлагается решить несколько задач уровня LeetCode Easy/Medium и пообщаться о внутрянке Python.

К сожалению, списывание и использование GPT на интервью лишь набирает обороты. Обычно это заметно довольно быстро:
- либо человек не может объяснить «своё же» решение;
- либо сыпется на каверзных вопросах про асимптотику, дополнительные ограничения и прочие нюансы.

Недавно узнал, что в бигтехах 🏙 во время интервью кандидату могут задать пару случайных дурацких вопросов:
- если человек честно говорит, что не знает - всё ок ;
- а вот если отвечает, то, как модно сегодня говорить, это редфлаг .

И буквально час назад у меня случилась идеальная иллюстрация этого подхода.

Кандидат ⭐️:
- шикарный опыт;
- почти 1 в 1 попадает в наш стэк;
- решает задачи раза в полтора быстрее всех прошлых кандидатов;
- знает абсолютно всё об asyncio;
- strong hire!

Но в какой-то момент в голове рождается мысль: а чем я хуже интервьюеров из бигтеха 😎?

И звучит вопрос:
- А расскажи мне, пожалуйста, про эвтектику в СУБД.

(эвтектику, если что, мне подсказал GPT - как что-то максимально умное, солидное и при этом абсолютно не к месту)

Кандидат без запинки отвечает, что проходил это ещё в вузе, и выдаёт какой-то поток несвязного бреда. Чувствую, что на подходе материал для поста (всё ради вас, подписчики ❤️), и решаю дожать:

- Супер. А откуда это вообще пошло? Что такое эвтектика в исходном смысле?

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

За пару минут собеседование Python-разработчика превратилось в устный экзамен по металлургии! Похоже, дурацкие вопросы работают! Иногда даже слишком хорошо 🤓

P.S. Эвтектика - это вполне реальный термин из металлургии и неорганической химии.

P.P.S. До сих пор не исключаю, что у человека первое образование было металлургическое 👀

С уважением,
Михаил Масягин
😁20🔥3👍2😱2🤣21👏1
«Мама, я в телевизоре 😎»

Ну, может и не в телевизоре, но с первым опытом студийной записи меня 😅

С уважением,
Михаил Масягин
6🔥19👍8👏4
This media is not supported in your browser
VIEW IN TELEGRAM
👏74🔥1🥰1
«Python 3.15 beta: что нового 🐍»

7 мая зафризили фичи Python 3.15, и сейчас, в длинные выходные, самое время обсудить ключевые изменения.
Сразу уточню, что полный стабильный релиз будет 1 октября, поэтому пока что катаемся на test- и debug- ENV-ах 🤓.

1. Lazy imports (PEP 810) 🥱

В язык завезли новое ключевое слово lazy. Ленивый модуль загружается только при непосредственном обращении к его коду, что ускоряет старт Python-процесса:

lazy import numpy as np
lazy from pandas import DataFrame

df = DataFrame() # только здесь pandas реально загрузится


Можно включить глобально через флаг -X lazy_imports=all или переменную PYTHON_LAZY_IMPORTS.

2. Распаковка в comprehensions (PEP 798)
📦

Самое долгожданное расширение синтаксиса за годы. Теперь * и ** работают внутри list/set/dict-comprehensions и генераторов:

lists = [[1, 2], [3, 4], [5]]
flat = [*L for L in lists] # [1, 2, 3, 4, 5]
merged = {**d for d in [{'a': 1}, {'b': 2}]} # {'a': 1, 'b': 2}


То, что раньше писалось через itertools.chain.from_iterable или вложенные циклы, теперь - одна строка. Работает и в async for.
Наконец вопрос на собесах «как разжать список списков» получил однозначный и окончательный ответ.

3. frozendict как builtin (PEP 814) 😎

«Замороженный» словарь - теперь встроенный тип. Можно класть в set, использовать ключом другого dict, да ещё и хэш не зависит от порядка вставки!

config = frozendict(host="localhost", port=5432)
cache = {config: "primary"}
hash(frozendict(a=1, b=2)) == hash(frozendict(b=2, a=1)) # True


Также его подружили с copy, json, pickle, pprint.

4. sentinel builtin (реализация PEP 661) 🛡

Все мы писали этот хак: _MISSING = object(), чтобы отличать «не передал» от «передал None». Теперь это часть языка:

MISSING = sentinel("MISSING")

def get(d, key, default=MISSING):
if default is MISSING:
raise KeyError(key)
return d.get(key, default)


Мелочь, а приятно.

5. Tachyon - сэмплирующий профайлер (PEP 799) 🔎

Появился пакет profiling с двумя бэкендами: profiling.tracing (бывший cProfile) и profiling.sampling - статистический профайлер с почти нулевым оверхедом. Самое крутое - сэмплирующий профайлер умеет подключаться к уже работающему процессу по его `PID`у:

python -m profiling.sampling --pid 12345 --format flamegraph -o out.svg


Кто хоть раз профилировал прод - понимает цену вопроса.

6.
Очередное ускорение 🚀

Ускорили JIT (да, в CPython есть JIT, хоть и по умолчанию он недоступен!) на 8-9% на x86-64 Linux и на 12-13% на AArch64 macOS.

Таким образом, 3.15 - это пусть и не «революционный», но важный релиз, значительно повышающий качество жизни разработчиков.
Стандартная библиотека продолжает вбирать в себя то, что годами жило в формате рецептов на Stack Overflow. Это ли не говорит о зрелости языка?

С уважением,
Михаил Масягин
🔥8👍63
«Cursor на миллиард 🤑»

В нашей команде мы активно используем множество ИИ-инструментов, в том числе Cursor. Сидим на Teams Plan. И сегодня я нашёл в этом «плане» неприятный сюрприз.

В Teams Plan команда представляет собой одного админа (Admin) и множество обычных пользователей (Member). При этом имеется возможность ограничить расход средств, выставив максимально допустимую месячную сумму, которую команда тратит на токены: превысил лимит - жди следующего месяца.

Но оказалось, что по умолчанию функция выставления лимитов доступна не только админу, но и любому члену команды! Да-да, не админу, не владельцу карты, а обычному Member-у! Имхо, это крайне неочевидное и небезопасное поведение, о котором документация упоминает лишь всколзь.

Заходишь в Settings → Spend Limit → Team Spend Limit, ставишь лимит в миллиард долларов 💵 и уходишь на ночь, запустив 1000 и 1 агента 😎.

Auto-моделька Cursor уверенно говорит, что это не баг, а фича, дабы «упростить онбординг команды» 😁

Чтобы запретить это веселье, нужно отдельно включить тумблер: Settings → Usage-Based Pricing Settings → Admin-only modifications.
После этого вкладка Spend Limit исчезает у обычных пользователей.

Интересная, конечно, помощь в онбординге команды...

С уважением,
Михаил Масягин

P.S. Может, имелся в виду онбординг команды топ-менеджеров Cursor на очередную яхту 🧐?
🤯8👍4😁42
«Data Lake: от перестановки мест слагаемых сумма... меняется? 👷»

Недавно проводил лекцию по DWH на курсе System Design от nevzorov.courses.

На лекции разбирали довольно частый практический кейс:
- есть ряд поддерживаемых источников данных (Sources);
- есть множество клиентов (Customers);
- для каждого клиента необходимо сохранять и обрабатывать данные из его источников (Customer Sources);
- вопрос: как лучше спроектировать Data Lake под эту задачу?


Вариант 1: customers/<customer_name>/source=<source_name>
Вариант 2: sources/<source_name>/customer=<customer_name>

Интуитивно рука тянется к 1 варианту... Однако для Data Lake и дальнейшей DWH-инфраструктуры часто лучше именно 2 вариант:

raw/sources/<source_name>/customer=<customer_name>/...
cleaned/sources/<source_name>/customer=<customer_name>/...
...


Например:

...
raw/sources/google_play/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=sabaton/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=megadeth/dt=2026-05-24/*.parquet
...
raw/sources/trustpilot/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=led_zeppelin/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=lordi/dt=2026-05-24/*.parquet
...


Почему?

1. Source естественным образом превращается в таблицу.

Для AWS Athena, Apache Trino или Apache Spark - google-play, trustpilot и т.д. - это отдельные логические таблицы, разложенные по Parquet-файлам и партициям в виде Customer'ов:

SELECT
*
FROM
"raw"."google_play"
WHERE
("customer" = 'rammstein') AND ("dt" >= DATE '2026-05-01');


У google-play даже в сыром виде (и уж тем более в очищенном) есть какая-то своя схема данных, ключи, timestamp'ы, правила дедупликации, SLA, логика инкрементальной загрузки и т.д.
У trustpilot и любого другого Source'а - свои.

Если же сделать наоборот:

...
raw/customers/rammstein/source=google_play/dt=2026-05-24/*.parquet
raw/customers/rammstein/source=trustpilot/dt=2026-05-24/*.parquet
...
raw/customers/sabaton/source=google_play/dt=2026-05-24/*.parquet
...


то один логический источник google-play размазывается по разным корням. А дальше начинается адъ 👹:
- отдельные таблицы на каждый Source каждого Customer'а;
- UNION ALL запросы и VIEW-шки;
- бардак с Data Governance.

В общем, Data Lake, а следом за ним и DWH медленно, но неотвратимо превращаются в DataSwamp 😄

2. Data Mesh проще делать именно по Source'ам.

Естественная единица владения - это не «папка клиента» (Customer), а доменный источник (Source).
У каждого такого Source'а есть отдельная команда-владелец, контракты, документация, SLA, data quality checks и правила эволюции схемы.

Команда, отвечающая за google_play, должна владеть одной папкой sources/google_play/customer=<customer_name>/*, а не тысячами подпапок customers/*/source=google_play/*.

- Добавили нового клиента? Добавили новую партицию.
- Поменяли контракт источника? Обновили один data product.
- Поймали баг в ingestion? Чиним одний единственный ETL-pipeline.

3. Pipeline'ы обычно тоже мыслят именно Source'ами:

...
google_play
trustpilot
...


А не:

...
ingest_rammstein_everything
ingest_sabaton_everything
ingest_led_zeppelin_everything
...


Иначе очень быстро появляются «особые клиенты»:
- у этого legacy CSV;
- у этого timezone в строке;
- у этого timestamp иногда null;
- у этого producer шлёт дубликаты;
- у этого «ну вы там руками поправьте, пожалуйста».

Поздравляю, у вас не DWH, а зоопарк с Airflow DAG-ами 🦓

4. Наконец, Source-First Layout упрощает сложную аналитику:

SELECT
"customer", count(*)
FROM
"raw"."google_play"
WHERE
"dt" = DATE '2026-05-24'
GROUP BY
"customer";


Можно с лёгкостью строить Usage-Based Billing по конкретным Source'ам, позволять даже менеджменту без труда копаться в данных и т.д.

Таким образом, проектируя DWH-систему лучше думать не о том, какие у вас будут клиенты, а о том, какие источники данных вы будете для них поддерживать.

С уважением,
Михаил Масягин
4🔥3🤔2💯2
«Финишная прямая 🎓»

Научный руководитель наконец одобрил текст диссертации, и сегодня я отнёс «кирпич» в 2-х экземплярах на финальную проверку на кафедру 😎!

Думаю, есть шанс, что первая предзащита будет в текущем учебном году (в июне).

С уважением,
Михаил Масягин

P.S. А ещё со следующего учебного года ассистент становится старшим преподавателем 😎
🔥25👏94
Приветствую Вас на канале P(hD)ython 👋

Меня зовут Михаил Масягин.

Я тимлид, разработчик, аспирант и преподаватель МГТУ им. Н.Э. Баумана.

Сейчас я руковожу backend - и frontend - разработкой в HFT - компании. До этого были Lawful Interception и Bare Metal - проекты, работа с AWS и даже погружение в ML и NLP.

С опытом я понял, что самые ценные знания обычно не попадают в учебники. Они появляются при решении реальных задач - через ошибки, багфиксы и дебаг, и, увы, часто теряются.


Именно поэтому появился этот канал. Здесь я буду делиться тем, что считаю реально полезным:

⚡️ Python и современные практики разработки
⚡️ оптимизация кода и performance engineering
⚡️ C, Linux и немного Bare Metal
⚡️ распределённые системы и архитектура
⚡️ алгоритмы и структуры данных
⚡️ HFT и инженерные решения из индустрии
⚡️ опыт из преподавания, аспирантуры и написания диссертации


Если Вам интересно не просто писать код, а понимать, почему он работает именно так, - добро пожаловать 🤝

С уважением,
Михаил Масягин
🔥21🤝9👍84
P(hD)ython pinned a photo