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

Автор - Михаил Масягин (@masyagin1998):
- Python Lead в NDA HFT;
- преподаватель в Бауманке;
- эксперт по СУБД System Design World;
- любитель PhD и авторегрессии.
Download Telegram
«Python для собесов - 2026 🚀»

На днях я понял, что хоть мой канал и называется P(hD)ython, на нём всё ещё нет ни одного полноценного поста про Python 😅.

Исправим это! Запускаю серию постов, которые помогут Вам при прохождении Python-собеседований в 2026 году 😎.

Один из моих любимых вопросов на Python-собесах звучит так: «если все ругают Python за скорость, то почему на нём пишут столько ML и математики?!». Удивительно, но часто на нём сыплются даже матёрые разработчики.

Отвечаю! Сам по себе Python и правда медленный (почему - отдельный вопрос), но когда дело доходит до тяжёлых вычислений, оказывается, что он вызывает функции, написанные на компилируемых языках вроде Rust, C\C++ или... Fortran!

Так, например, numpy по сути является обёрткой над С-массивами, а scipy.linalg.lapack обычно импортирует Fortran-библиотеку LAPACK! Даже pytorch, исполняясь на GPU, по факту делает это на C\C++ & CUDA ⚙️.

Всё это достигается за счёт Foreign Function Interface (FFI) - механизма, позволяющего коду на одном языке вызывать функции, написанные на другом. FFI есть не только в Python. Он имеется в Rust (extern "C" & unsafe), Java (JNI & JNA), Go (cgo) и т.д., так что идея этого поста легко обобщается и на другие языки.

А теперь пример! Сегодня мы напишем свой собственный numpy с поэлементным умножением float-векторов!

Сперва реализуем функцию fmul поэлементного умножения float-векторов на C в файле fmul.c:

#include <stddef.h>
void fmul(const float* a, const float* b, float* out, size_t n) {
for (size_t i = 0; i < n; ++i) out[i] = a[i] * b[i];
}


Скомпилируем его как разделяемую библиотеку:

gcc -O3 -fPIC -shared -o libfmul.so fmul.c


Установим cffi для поддержки FFI в Python:

pip install cffi


Теперь напишем Python-класс для работы с float-векторами и fmul в файле fmul.py:

from array import array
from pathlib import Path
from cffi import FFI

# через FFI подгружаем разделяемую библиотеку в Python-код
ffi = FFI()
ffi.cdef("void fmul(const float* a,const float* b,float* out,size_t n);")
C = ffi.dlopen(str(Path(__file__).with_name("libfmul.so")))

class F32Vec:
# используем __slots__ для более компактного и производительного представления объектов класса
__slots__ = ("a",)

# требуем, чтобы наш класс конструировался из float-массива Python
def __init__(self, data):
self.a = (
data
if isinstance(data, array) and data.typecode == "f"
else array("f", data)
)

def __len__(self):
return len(self.a)

def _mul_arr(self, other):
if len(self.a) != len(other):
raise ValueError("len mismatch")

# готовим выходной массив
out = array("f", [0.0]) * len(self.a)

# вызываем через FFI C-функцию fmul
C.fmul(
ffi.from_buffer("float[]", self.a),
ffi.from_buffer("float[]", other),
ffi.from_buffer("float[]", out),
len(self.a),
)
return F32Vec(out)

# перегружаем операторы умножения, чтобы поэлементное произведение работало как в numpy
def __mul__(self, other):
if isinstance(other, F32Vec):
return self._mul_arr(other.a)
if isinstance(other, (int, float)):
return self._mul_arr(array("f", [float(other)]) * len(self.a))
return self._mul_arr(array("f", other))

__rmul__ = __mul__

def tolist(self):
return list(self.a)

def __repr__(self):
return f"F32Vec({list(self.a)})"


if __name__ == "__main__":
x = F32Vec([0.0, 0.5, 1.0, 1.5])
y = F32Vec([1.0, 2.0, 3.0, 4.0])

print(x._mul_arr(y.a))
print((x * y).tolist())
print((x * 2.0))
print((2.0 * x).tolist())


Наконец, запустим код:

python3 fmul.py
F32Vec([0.0, 1.0, 3.0, 6.0])
[0.0, 1.0, 3.0, 6.0]
F32Vec([0.0, 1.0, 2.0, 3.0])
[0.0, 1.0, 2.0, 3.0]


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

С уважением,
Михаил Масягин
Please open Telegram to view this post
VIEW IN TELEGRAM
13🔥10🏆51
«Python для собесов - 2026 🚀»

Продолжаем подготовку к Python-собесам!

В прошлом посте мы перенесли тяжёлую функцию из Python в C, используя механизм FFI. Сегодня мы научимся делать это более элегантно 💅 посредством Python-компиляторов, таких как Numba, Pythran или JAX. Они компилируют подмножество Python в машинный код для CPU или GPU, тем самым повышая производительность и оставаясь в рамках Python.

У каждого из приведённых инструментов своё собственное поддерживаемое подмножество Python, но базово можно считать, что все они способны компилировать функции, принимающие на вход, порождающие внутри себя и возвращающие:
- numpy-структуры данных, исключая object;
- Python-примитивы (int, float, complex, bool, str, ...);
- реже - гомогенные числовые списки (List[float], List[int], ...).

Python-компиляторы работают следующим образом:
- Вы пишете Python-функцию и помечаете её компилируемой с помощью декоратора (Numba, JAX) или комментария (Pythran).
- Далее, если компилятор работает как JIT (Just-in-Time), то он... ничего не делает 😄 (Numba, JAX), а вот AOT-компилятор сразу приступает к «конвертации» функции в машинный код (Pythran). Недостатком второго подхода является то, что он требует задавать типы входных данных на этапе написания кода. Кстати, в Numba так тоже можно сделать.
- При исполнении Python-кода в случае AOT'а сразу запускается «бинарная» версия функции, а в случае JIT'а - функция компилируется при первом вызове, а при последующих уже используется её производительная версия. В связи с этим JIT'ованные функции важно «прогревать» - вызывать их с примером входных параметров при инициализации приложения, дабы исключить задержки компиляции при исполнении. Отмечу, что Numba и Pythran налагают ограничения только на типы входных данных, а JAX - ещё и на их размер, то есть для Numba и Pythran вызовы func([1, 2, 3]) и func([4, 5]) идентичны, а для JAX они, грубо говоря, приводят к двум разным функциям.

Пара важных нюансов:
- Переходы между Python и скомпилированным кодом дороги. Чтобы их минимизировать, следует делать компилируемые функции крупнее. Например, если в коде есть обработка данных в цикле, то лучше компилировать цикл целиком.
- Чтобы не бить проект на отдельные компилируемые функции, можно использовать режим jit в Numba: он скомпилирует лишь то, что сможет. К сожалению, его производительность непредсказуема.

К примерам! Напишем функцию fmul из прошлого поста на Numba и Pythran. А на JAX можете написать сами 😄.

Numba:

Поставим Numba:

pip install numba


Затем создадим файл numba_demo.py:

import numpy as np
from numba import njit

@njit
def fmul_numba(a, b, out, n):
for i in range(n):
out[i] = a[i] * b[i]

out = np.empty(4, dtype=np.float64)
fmul_numba(np.array([0.0, 0.5, 1.0, 1.5]), np.array([1.0, 2.0, 3.0, 4.0]), out, 4)
print(out)


Запустим его:

python3 numba_demo.py
[0. 1. 3. 6.]


Pythran:

pip install pythran


fmul_pythran.py:

# pythran export fmul_pythran(float[], float[], float[], int)
def fmul_pythran(a, b, out, n):
for i in range(n):
out[i] = a[i] * b[i]


pythran_demo.py:

import numpy as np
from fmul_pythran import fmul_pythran
import sys

print(sys.modules["fmul_pythran"])

out = np.empty(4, dtype=np.float64)
fmul_pythran(np.array([0.0, 0.5, 1.0, 1.5]), np.array([1.0, 2.0, 3.0, 4.0]), out, 4)
print(out)


pythran fmul_pythran.py
ls
fmul_pythran.cpython-312-x86_64-linux-gnu.so fmul_pythran.py pythran_demo.py
python3 pythran_demo.py
<module 'fmul_pythran' from './fmul_pythran...so'>
[0. 1. 3. 6.]


Кстати, пару лет назад на одном из собесов мне пришлось писать Numba-код, так что это даже более актуально, чем FFI 🤓.

Сегодня мы познакомились с Python-компиляторами, в следующем посте поговорим о том, как понять, какие именно функции нам надо компилировать! ⚙️🚀

С уважением,
Михаил Масягин
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍4💯3
«Сдеаноню NDA HFT на 300+ человек 😈😬»

Уже в это воскресенье я буду выступать с живой лекцией на 300+ человек на четвертьфинале ICPC в Сколково и публично расскажу о том, как построить современную и прибыльную HFT-инфраструктуру с нуля 😎!

Присоединяйтесь: https://t.me/moscowregional_news/48 🚀!

О чём поговорим:
- как выглядит живой пайплайн HFT изнутри, а не в учебных статьях;
- почему shared memory решает;
- что такое hot- и cold-path;
- что точно должен уметь делать ваш бэктест;
- и ещё о миллионе технических нюансов!
- ах да, и о том, где лучше работать в HFT 🤑

Если вдруг вы будете среди этих 300+ человек (а я точно знаю, что в этом канале сидит несколько олимпиадников ❤️), подходите после доклада - поболтаем про HFT, борьбу за микросекунды и вот это вот всё наше!

P.S. А ещё у нас будет свой собственный олимпиадный контест с уклоном в HFT и стенд с рафом, матчей и чётким мерчом для чётких разработчиков 😎

С уважением,
Михаил Масягин
🔥1310💯32
«HFT-доклад прошёл по плану 😎»

День Московского четвертьфинала ICPC позади 😌

Поговорили с многими десятками студентов, раздали тонны мерча и ещё больше ребят завлекли на наш контест 😈

Я очень волновался перед выступлением, но всё прошло просто супер! Были и доп. вопросы, и бурные овации! ❤️

Особенно приятно было видеть студентов из родной Бауманки!  Ребята - вы лучшие! 🔥

P.S. Больше фото в комментариях!

P.P.S. А вот и видео https://youtu.be/i8GXmeoMmn8?t=28055 !

С уважением,
Михаил Масягин
🔥185💯4🤩1
«Горячий HFT-доклад 🔥»

Выложил запись своего выступления про HFT-инфраструктуру с Moscow ICPC 2025 на YouTube!

https://youtu.be/us53niWItTg?si=UwSvvEsaJMDFJ_RM

Смотрите, комментируйте, поддерживайте лайками и репостами!

В докладе про HFT, FPGA, Kernel-Bypass и много-много денег 🤑!

P.S. Сразу после выступления очень сильно заболел, поэтому видео немного задержалось. Но лучше поздно, чем никогда 💪!

С уважением,
Михаил Масягин
🔥137👍5👏1
«Эффект попугая у публичных спикеров 🦜»

Периодически посматриваю лекции и выступления известных учёных и программистов - на YouTube, в подкастах, на конференциях и т.д.

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

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

https://t.me/studsovet_iu/1319

Уже в следующую среду читаю расширенный доклад о современной HFT-инфраструктуре в Бауманке. Постепенно превращаюсь в профессионального спикера. Обещаю, что добавлю хотя бы один новый слайд и пару новых шуток, но это не точно 😄

P.S. Помню о рубрике «Python для собесов - 2026 🚀». На выходных выложу новый пост 🤝!

С уважением,
Михаил Масягин
🔥86💯3👍1
«Python для собесов - 2026 🚀»

Продолжаем серию постов для Python-собесов!

В прошлых постах мы научились ускорять Python (FFI & Python Compilers). Теперь поговорим о том, что именно нужно ускорять, потому что оптимизировать абсолютно всё - сложно, долго и дорого (да зачастую и не нужно) 😵.

Базовый алгоритм выглядит следующим образом:
1. Замерили время выполнения кода.
2. Нашли узкое место.
3. Оптимизировали.
4. Проверили, что стало лучше. Если нет или недостаточно, то возвращаемся к пункту 1 😄.

Начнём с простейших измерений.

На собесах часто спрашивают как в коде замерить время выполнения отдельной функции. К сожалению, ответ time() встречается неприлично часто. Однако time() это так называемые «wall clock» 🕚 - они не монотонны (могут перепрыгивать из-за NTP, смены поясов, гибернации), а их точность и разрешение невысоки и зависят от настроек ОС. Вместо них лучше использовать монотонные счётчики:
- perf_counter_ns() - точный и монотонный аналог time() (для общих случаяев);
- process_time_ns() - только время на CPU (удобен для числодробилок).

Пример кода:

from time import perf_counter_ns as pc_ns

def work(n):
s = 0
for i in range(n):
s += i*i
return s

if __name__ == "__main__":
t = pc_ns()
work(1000000)
dt = pc_ns() - t
print(f"{dt/1e6:.3f} ms")


и его запуск:

python3 pc_ns_demo.py 
23.767 ms


Один прогон не слишком показателен - виной тому «шум» планировщика, кэши, вызовы GC и т.д. diff счётчиков можно сохранять в массив в цикле, но лучше использовать готовый инструмент - timeit. Он позволяет запускать много раз функции или даже программы, задавая как число прогонов в замере (number), так и число самих замеров (repeat), тем самым получая устойчивое распределение времени.

Пример кода:

import timeit
from pc_ns_demo import work

rs = timeit.repeat(lambda: work(1000000), number=10, repeat=10)

print("runs:")
for i, t in enumerate(rs, 1):
print(f" {i:02d}: {t/10*1e3:.3f} ms")

bs = min(rs)
print(f"min: {bs/10*1e3:.3f} ms")


и его вызов:

python3 timeit_demo.py 
runs:
01: 23.849 ms
...
10: 23.650 ms
min: 23.650 ms


Ещё короче:

python3 -m timeit -n 10 -r 10 -u msec -s "from pc_ns_demo import work" "work(1000000)"
10 loops, best of 10: 23.5 msec per loop


Поговорим о профилировании - определении конкретных медленных участков кода с cProfile. Он позволяет подсчитать число вызовов каждой функции (ncalls), её целевое и суммарное время исполнения (без/с учётом вызовов функций внутри) (tottime, percall, cumtime, percall) с привязкой к определению (filename:...).

Запуск профайлера для программы:

python3 -m cProfile -o prof.bin pc_ns_demo.py
24.467 ms

python -c "import pstats;p=pstats.Stats('prof.bin');p.sort_stats('tottime').print_stats(20)"
Fri Dec 19 06:00:17 2025 prof.bin

7 function calls in 0.024 seconds

Ordered by: internal time

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.024 0.024 0.024 0.024 pc_ns_demo.py:3(work)
...


Файл prof.bin можно визуализировать пакетом snakeviz - скрин в комментах.

И внутри python-кода:

import cProfile
import pstats
from io import StringIO
from pc_ns_demo import work

def kek(a, b, c):
return work(a) + work(b) + work(c)

pr = cProfile.Profile()
pr.enable()
print(kek(1000000, 2000000, 3000000))
pr.disable()

s = StringIO()
pstats.Stats(pr, stream=s).sort_stats("cumtime").print_stats(5)
print(s.getvalue())


и запуск:

python3 cprofile_demo.py
11999993000001000000
6 function calls in 0.146 seconds

Ordered by: cumulative time

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.146 0.146 /home/mikhail/cprofile_demo.py:6(kek)
3 0.146 0.049 0.146 0.049 /home/mikhail/pc_ns_demo.py:3(work)
...


Существуют и другие инструменты вроде py-spy и scalene. Они позволяют периодически дампить callstack процесса, а scalene показывает ещё и аллокации 🤓. Вот мы и познакомились с джентльменским набором тайминга и профайлинга в Python! ⚙️🚀

С уважением,
Масягин Михаил
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍4💯311
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