es
Feedback
About Python [ru]

About Python [ru]

Ir al canal en Telegram

Пишем на Python, создаём нейросети и ИИ-агентов. Алгоритмы, задачи и вайбкодинг. Личный блог автора - @just_genych По вопросам рекламы или разработки: @g_abashkin

Mostrar más
6 489
Suscriptores
+124 horas
+27 días
-4330 días
Archivo de publicaciones
Прошла половина лета Выгореть ещё успеем. А пока лето — отправляемся загорать. И не просто так, а со скидкой 5000 рублей по п
Прошла половина лета Выгореть ещё успеем. А пока лето — отправляемся загорать. И не просто так, а со скидкой 5000 рублей по промокоду NETSIL от Яндекс Путешествий. Забронировать #реклама travel.yandex.ru О рекламодателе

Zero-downtime миграции схем в SQLAlchemy 2.0: партиционирование и transactional DDL в Alembic Когда таблица переваливает за 100 миллионов строк, обычная миграция через ALTER TABLE превращается в план на выходные с даунтаймом. Если повезёт. Почему transactional DDL критичен PostgreSQL умеет выполнять DDL внутри транзакций — это transactional DDL. Alembic это поддерживает, но при партиционировании важно не забыть про autocommit_block(). Без него некоторые операции (создание партиций) могут упасть вне транзакции, и сервер уйдёт в простой. Expand-contract стратегия Вот как выглядит zero-downtime подход: Фаза 1 (Expand) — создаём новую партиционированную таблицу параллельно старой. Не трогаем существующую схему. Фаза 2 (Migrate) — организуем двойную запись. Апдейты пишутся и в старую, и в новую структуру. Через триггеры или приложение. Фаза 3 (Contract) — переключаем чтение на новую схему. Удаляем старую таблицу, когда убедились, что всё работает. В коде Alembic это выглядит примерно так:
def upgrade():
    with op.get_context().autocommit_block():
        op.execute("""
            CREATE TABLE orders_new (LIKE orders INCLUDING ALL)
            PARTITION BY RANGE (created_at)
        """)
    # Дальше — батчевая вставка данных с паузами
Главные грабли * Deferred constraints. Если есть внешние ключи, их лучше отключать на время миграции, иначе потом не запихнете данные. * Batch processing. Не пытайтесь перелить 100M строк одним INSERT. Делайте чанками по 1000 записей с time.sleep(0.1). Иначе транзакция отожмёт всё. * Versioned migrations. Сохраняйте старые партиции хотя бы неделю после переключения. На проде всегда вылезает "ой, а мы забыли перенести поле". Production-oriented пример Создаём теневую таблицу, льём данные батчами, потом атомарно переименовываем:
def upgrade():
    op.create_table('orders_shadow',
        sa.Column('id', sa.Integer, primary_key=False),
        postgresql_partition_by='RANGE (created_at)'
    )
    connection = op.get_bind()
    total = connection.execute("SELECT count(*) FROM orders").scalar()
    for offset in range(0, total, 1000):
        connection.execute(f"""
            INSERT INTO orders_shadow 
            SELECT * FROM orders 
            ORDER BY id LIMIT 1000 OFFSET {offset}
        """)
        time.sleep(0.1)
    connection.execute("""
        ALTER TABLE orders RENAME TO orders_old;
        ALTER TABLE orders_shadow RENAME TO orders;
    """)
Предостережения, которые обычно игнорируют * Тестируйте на клоне прода. Не на тестовой базе с 10 строками. * Используйте --sql флаг Alembic, чтобы посмотреть, что он сгенерирует, и руками проверить план. * Мониторьте log_statement и deadlocks. Партиционирование не спасает от блокировок при массовом INSERT. * Имейте rollback-скрипт для каждой фазы. Если что-то пошло на этапе переименования, откат должен быть за секунды. Вывод: Партиционирование через transactional DDL — это не магия, а расчёт, который окупается, когда таблицы растут на 10% в месяц, иначе проще оставить как есть.

💡 CSV-отчёты с параметрами и без таймаута: быль из BI Один чувак рассказал, как он вкалывал в BI крупного банка. Начальство
💡 CSV-отчёты с параметрами и без таймаута: быль из BI Один чувак рассказал, как он вкалывал в BI крупного банка. Начальство тащилось от дашбордов и красивых графиков, а вот тем, кто реально возился с данными, приходилось лезть в выгрузки и разбираться, что там пошло не так — баг ли, задержка загрузки, или формула мудрит. У финансистов не было доступа к базе, поэтому они таскали данные в Excel, сравнивали выгрузки и вручную проверяли метрики. Под них замутили первую версию софта: он клепал отчёты в Excel и сохранял историю выгрузок на сервере. Читать далее 👉 About Python

Онлайн-магистратура для IT: ИТМО, МИФИ + Яндекс Программы онлайн-магистратуры ИТМО и МИФИ в партнёрстве с Яндексом. Актуальные знания, практическое обучение и гибкий график. Учитесь, совмещая с работой. Доступна господдержка оплаты, отсрочка от армии Перейти на сайт #реклама 16+ practicum.yandex.ru О рекламодателе

Zero-Copy Обёртки Над Буферами: Парсинг Бинарных Протоколов Через __buffer__ и __release_buffer__ Каждый раз, когда дамп сырого memoryview из сокета копируется в bytes для парсинга struct, теряется время — на production с десятками тысяч пакетов в секунду это становится узким местом. В Python 3.12+ протокол буфера позволяет реализовать zero-copy классы-обёртки без копирования данных. Зачем нужны типизированные обёртки Сырой memoryview — это всего лишь как бы массив байтов, где нужно помнить смещения и форматы. Класс с полями даёт типобезопасность и читаемость, при этом внутри работает с тем же буфером. Например, для фиксированного заголовка пакета: 4 байта ID, 2 байта длины, 2 байта флагов.
import struct

class PacketHeader:
    def __init__(self, buffer):
        self._buffer = buffer

    def __buffer__(self, flags):
        return self._buffer.__buffer__(flags)

    def __release_buffer__(self, buffer):
        pass

    @property
    def packet_id(self):
        return struct.unpack_from('<I', self._buffer, 0)[0]

    @property
    def length(self):
        return struct.unpack_from('<H', self._buffer, 4)[0]
Zero-copy парсинг в действии Вместо копирования через bytes(data) используем MSG_PEEK и передаём memoryview напрямую:
raw_data = memoryview(sock.recv(8, socket.MSG_PEEK))
header = PacketHeader(raw_data)
print(header.packet_id, header.length)
Нет ни одного лишнего копирования. Это особенно критично для high-throughput протоколов в связке с asyncio, где каждый микросекунда на аллокацию — потеря. Типичная ошибка и trade-offs Управление временем жизни буфера — это головная боль. Если исходный memoryview будет уничтожен раньше обёртки, вы получите segfault или мусорные данные. Всегда явно контролируйте scope: держите ссылку на исходный буфер до завершения парсинга. Вывод: Протокол __buffer__ и __release_buffer__ дают zero-copy доступ к бинарным данным без копирования, но требуют аккуратного менеджмента памяти и не заменяют быстрый struct.unpack для разовых задач.

Бесплатный курс: веб-дизайн, графика, интерфейсы Научись создавать дизайн сайтов и приложений, инфографику для карточек на маркетплейсах и работать в Figma! Студенты курса в среднем зарабатывают от 68 000 ₽ уже во время обучения💰 Этот курс для тебя, если ты: ✅ мечтаешь о новой профессии, но не знаешь, с чего начать; ✅ чувствуешь, что хочешь большего — свободы, самореализации, творчества; ✅ полный новичок и хочешь систему, а не хаос; ✅ хочешь начать зарабатывать удалённо. Зарегистрироваться #реклама 16+ ydaev.ru О рекламодателе

Какой кригинг выбрать: простой, ординарный, с трендом, блочный, индикаторный Мы создаем софт для горно-геологических служб ка
Какой кригинг выбрать: простой, ординарный, с трендом, блочный, индикаторный Мы создаем софт для горно-геологических служб калийных рудников, и после первой статьи про кригинг на чистом NumPy самый частый вопрос звучал одинаково: «Хорошо, а какой именно кригинг брать?» Вопрос правильный: под словом «кригинг» живёт целое семейство методов, и выбор между ними влияет на результат сильнее, чем тонкая настройка вариограммы. В плагине Isoliner их пять - простой, ординарный, с полиномиальным трендом, блочный и индикаторный, - и каждый существует не для галочки, а под конкретный класс геологических задач. Все пять видов кригинга решают систему: оценка в точке — взвешенная сумма соседних скважин, веса — решение системы уравнений с ковариациями из вариограммы. Различаются они тем, что считается неизвестным про среднее поле и что именно оценивается — точка, блок или вероятность. Простой кригинг предполагает, что среднее значение поля известно заранее и постоянно по площади, и недобор веса соседей компенсируется этим средним. Читать далее

Сидеть и работать в корпорации страшно, жизнь-то мимо проходит. Уходить строить бизнес страшно, а вдруг прогорит. Один из вариантов - разрабатывать свой пет-проект по вечерам. Многие успешные компании, например, Twitter, создавались именно так. Это не значит, что ваш проект обязательно заработает миллиарды, но заработать больше, чем в найме, и получить ценный опыт вполне реально. Перед началом разработки появляется множество вопросов, например: – Как выбрать идею для пет-проекта? – Что нужно знать про маркетинг? – Как запуститься и довести до первых продаж не имея бюджета на рекламу? В телеграм-канале Твой пет проект, Михаил Табунов делится своим опытом с разработчиками и менеджерами. Он рассказывает, где искать идею для нового проекта, что нужно знать о маркетинге, как запустить стартап и привлечь первых 10 клиентов, а также о многих других важных вещах. Подписывайтесь на Твой пет проект получайте пользу от практиков рынка!

Yandex Ecom Open Air — летнее событие про онлайн-продажи Yandex Ecom Open Air 2026 объединяет деловую программу, живое общение и атмосферу фестиваля в одном потоке. Здесь обсуждают технологии, которые становятся частью среды. Исследуют силы, которые влияют на весь рынок. Находят новые связи, идеи и точки роста. В течение дня пространство фестиваля наполняют выступления, дискуссии, встречи, специальные форматы, музыка и активности партнёров. Присоединиться к происходящему можно из любой точки — на площадке фестиваля или через онлайн-трансляцию. Зарегистрироваться #реклама 18+ ecomfest.ru О рекламодателе

Graceful Shutdown в asyncio: как не потерять данные при SIGTERM в production Стандартный signal.signal блокирует event loop, превращая асинхронное приложение в синхронный ступор. Решение - loop.add_signal_handler(), но без pipe вы рискуете утечкой ресурсов: воркеры не успеют закрыть соединения или снять блокировки. Проблема: почему просто cancel() не работает Сигналы обрабатываются в том же потоке, что и цикл событий. Если в обработчике сразу отменять корутины, вы получите состояние гонки: одни задачи завершатся, другие - нет. Ресурсы утекут, соединения повиснут. Пример частой ошибки:
def handler():
    for task in asyncio.all_tasks():
        task.cancel()  # Блокировка в синхронном контексте
Решение: pipe как мост между синхронным и асинхронным миром Используйте os.pipe() для безусловного пробуждения цикла. Обработчик сигнала только пишет байт в pipe - никаких корутин или блокировок. Через add_reader асинхронный контекст подхватывает запись и выполняет graceful shutdown:
class GracefulShutdown:
    def __init__(self):
        self.loop = asyncio.get_event_loop()
        self.r_fd, self.w_fd = os.pipe()
        self.loop.add_reader(self.r_fd, self._handle_stop)
        for sig in (signal.SIGTERM, signal.SIGINT):
            self.loop.add_signal_handler(sig, self._signal_handler)

    def _signal_handler(self):
        os.write(self.w_fd, b'\x00')  # Только запись
Критичное правило: никакой логики в _signal_handler Запись в pipe - единственная операция. Закрытие дескрипторов, отмена задач, asyncio.all_tasks() - все это делается в асинхронном _handle_stop, где task.cancel() пробрасывает CancelledError в воркеры, давая шанс выполнить cleanup. Если нарушить это правило в многопоточном run_in_executor, словите блокировку цикла. Trade-off: pipe vs call_soon_threadsafe loop.call_soon_threadsafe тоже работает, но pipe надежнее в сценариях с воркерами в других потоках: он гарантированно будит именно тот цикл, который слушает. Без pipe вы рискуете, что сигнал пропустит цикл, занятый долгим await. Вывод: Pipe + add_signal_handler превращает SIGTERM из источника утечек в управляемое завершение, где каждый воркер закрывает ресурсы через CancelledError.

Рабочее приложение, которое прошло ревью, всё ещё можно взломать через форму логина. Большинство уязвимостей в вебе — это тип
Рабочее приложение, которое прошло ревью, всё ещё можно взломать через форму логина. Большинство уязвимостей в вебе — это типовые ошибки, которые находят уже после инцидента. Курс WAPT от Codeby учит находить их раньше — с позиции атакующего. Этот курс максимально заточен на практику: 65 заданий в лаборатории, где вы отрабатываете каждую технику на реальных сценариях. Что внутри: ⏺️Эксплуатация актуальных классов уязвимостей, активный и пассивный фаззинг ⏺️SQL Injection, CMD Injection, XSS, PHP Injection, Server-Side Template Injection ⏺️Повышение привилегий и client-side атаки (XSS, CSRF) Каждую неделю — вебинары с куратором. Навыки применимы и в рабочих задачах по пентесту, и в Bug Bounty. Курс стартует 16 июля. ➡️ Запись на курс По всем вопросам: @CodebyAcademyBot

Платформа биллинга для облачного провайдера Перепродажа хостинга и доменов. Автоматизация продаж. Биллинг ✅ Хостинг под ключ
Платформа биллинга для облачного провайдера Перепродажа хостинга и доменов. Автоматизация продаж. Биллинг ✅ Хостинг под ключ Готовое решение без затрат на разработку и кастомизацию ✅ Облачный биллинг Продавайте услуги VPS/VDS, облачные ресурсы и доп. сервисы ✅ Продажа сертификатов и доменов Повышайте средний чек, предоставляя дополнительные сервисы ✅ Система поддержки клиентов Тикет-система с контролем качества, база знаний ✅ Встроенная BI-система Прогноз затрат и развитие бизнеса, дополнение аналитики Узнать больше #реклама 16+ ispsystem.ru О рекламодателе

Типизированные TypeVar и ParamSpec для generic-коллбэков: статическая проверка обработчиков событий без Protocol и лишних абстракций Типизация коллбэков — вечная боль production-кода, особенно при реализации диспетчеров событий или callback-based middleware. Частая ошибка: использовать Callable[..., Any], теряя сигнатуру и получая TypeError на проде. Проблема
def register(handler: Callable[..., Any]):
    ...
Здесь анализатор не проверяет аргументы. В production это приводит к ошибкам при первом несовпадении типов — например, когда обработчик ожидает (int, str), а передается (float, User). Решение
from typing import TypeVar, ParamSpec, Callable

P = ParamSpec("P")
T = TypeVar("T")

def register(
    handler: Callable[P, T],
    *args: P.args,
    **kwargs: P.kwargs,
) -> T:
    return handler(*args, **kwargs)
Статический анализатор теперь видит точную сигнатуру коллбэка. Никаких сюрпризов. Реальный пример: диспетчер событий
class EventDispatcher:
    def on(self, event: str) -> Callable[[Callable[P, T]], Callable[P, T]]:
        def wrapper(handler: Callable[P, T]) -> Callable[P, T]:
            self.handlers[event] = handler
            return handler
        return wrapper

dispatcher = EventDispatcher()

@dispatcher.on("user_login")
def handle_login(user_id: int, timestamp: float) -> str:
    return f"User {user_id} logged in at {timestamp}"

# Правильно
result = dispatcher.handlers["user_login"](42, 1689000000.0)

# Ошибка типов: expected float, got str
result = dispatcher.handlers["user_login"](42, "bad")
Почему это лучше Protocol? * Меньше кода — не надо объявлять абстрактные классы с __call__ * Точность — сохраняются имена параметров, порядок, типы и возвращаемое значение * Универсальность — один декоратор работает с любыми сигнатурами * Совместимость — работает с callback-based либами вроде functools или asyncio Когда Protocol все же нужен? Если коллбэк должен иметь атрибуты (например, handler.priority = 5) или наследовать несколько абстракций. По опыту — в 80% случаев это избыточно. Вывод: ParamSpec + TypeVar дают чистую и проверяемую типизацию generic-коллбэков без лишних сущностей — замените Callable[..., Any] на точные сигнатуры на ревью.

Отказы на Android-собесах? Разбираю ошибки джунов и мидлов. Мок-интервью, ревью кода, план до оффера. 8 лет в Android, ментор
Отказы на Android-собесах? Разбираю ошибки джунов и мидлов. Мок-интервью, ревью кода, план до оффера. 8 лет в Android, ментор Яндекса. Узнать больше #реклама О рекламодателе

Сборка колл-стеков для профилирования CPU в production через sys.setprofile и signal.setitimer без внешних трейсеров Когда прожорливый код угробит CPU в продакшене, а ставить py-spy или Pyroscope нельзя из-за политики безопасности или ограничений архитектуры, поможет связка стандартной библиотеки. Разработчики часто забывают, что семплер можно собрать за час без единой внешней зависимости, но допускают ошибки с потоками и задержками. Идея и реализация signal.setitimer запускает обработчик через заданные интервалы (например, 50-100 мс). Внутри него sys._current_frames() делает мгновенный снапшот стеков всех потоков. Ключевой нюанс: не используй traceback.format_stack() — это тормозит. Только сухие f_code.co_name и f_lineno. Production-ready пример для мониторинга CPU-горячих точек
import signal
import sys
import threading
import time

stacks_buffer = []
BUFFER_LOCK = threading.Lock()

def collect_stack(signum, frame):
    try:
        snapshot = {}
        for tid, t_frame in sys._current_frames().items():
            stack = []
            while t_frame:
                stack.append(f"{t_frame.f_code.co_name}:{t_frame.f_lineno}")
                t_frame = t_frame.f_back
            snapshot[tid] = stack[:15]  # Ограничиваем глубину
        with BUFFER_LOCK:
            stacks_buffer.append((time.time(), snapshot))
    except Exception:
        pass  # Тихий сброс ошибок

signal.signal(signal.SIGALRM, collect_stack)
signal.setitimer(signal.ITIMER_REAL, 0.05, 0.05)  # 50 ms

# Далее обычный код приложения...
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    signal.setitimer(signal.ITIMER_REAL, 0, 0)
    for ts, snap in stacks_buffer[:5]:
        print(ts, snap)
Типичные ошибки и trade-offs * Сигнал SIGALRM работает только в главном потоке — для многопоточности нужен ручной signal.signal и отдельный поток-коллектор для записи в буфер (иначе потеря данных из-за рекурсии сигналов). * Минимальная частота — 50 мс; ниже уже жрёт CPU сам семплер. Для asyncio проще использовать asyncio.Task.get_stack() из цикла событий. * C-расширения (например, numpy или lxml) не покажут стек — тут без py-spy или perf не обойтись. Практический совет Записывай в кольцевой буфер с фиксированной ёмкостью (например, 1000 сэмплов) и асинхронно сбрасывай на диск в фоновом потоке — это thread-safe и не фризит основной код. Вывод: Связка sys.setprofile + signal.setitimer даёт дешёвый семплинг стека для поиска CPU-горячих точек в продакшене без внешних зависимостей, но требует аккуратности с потоками, частотой и обработкой C-расширений.

Вакансия Альфа-Агент — в команде Альфа-Банка Станьте Альфа-Агентом — работа в прямых продажах у лучшего работодателя страны.
Вакансия Альфа-Агент — в команде Альфа-Банка Станьте Альфа-Агентом — работа в прямых продажах у лучшего работодателя страны. Присоединяйтесь к команде ❤️ Конкурентоспособная зарплата, расширенный ДМС со стоматологией, программа поддержки первые 3 месяца. Карьера в ваших руках! Смотрите актуальные вакансии на официальном сайте Альфа-Банка. Перейти на сайт #реклама job.alfabank.ru О рекламодателе

Геостатистика в QGIS без SAGA: кригинг на чистом NumPy Автор статьи делится опытом создания софта для горно-геологических слу
Геостатистика в QGIS без SAGA: кригинг на чистом NumPy Автор статьи делится опытом создания софта для горно-геологических служб калийных рудников. Геологи и маркшейдеры ежедневно превращают тысячи скважинных проб в карты: отметки кровли пласта, содержания KCl, мощности, газоопасность. Классический инструмент для этого — кригинг, который в QGIS формально есть через SAGA, GRASS, Smart-Map и связки со SciPy. Однако каждый из этих вариантов чем-то не устраивал, и год назад автор начал писать свой плагин. Сейчас плагин Isoliner включает 24 инструмента в официальном репозитории plugins.qgis.org: кригинг четырёх видов, вариограммный анализ, кросс-валидация с отчётами, изолинии с контурными полигонами, геологические разрезы и собственный 3D-просмотр. Вычислительное ядро построено на чистом NumPy без внешних зависимостей. В статье также объясняется, зачем понадобился ещё один кригинг, как выглядит система кригинга в двадцати строках NumPy, что такое вариограмма на пальцах и почему абсолютные единицы силла — главные грабли для новичков. Читать далее на Хабре

Аренда VPS/VDS-сервера. Виртуальные выделенные серверы в дата-центрах уровня Tier III — 7 готовых конфигураций от 200 ₽/мес.
Аренда VPS/VDS-сервера. Виртуальные выделенные серверы в дата-центрах уровня Tier III — 7 готовых конфигураций от 200 ₽/мес. Преимущества аренды: - Выделенные ресурсы без переплаты; - KVM-виртуализация; - Быстрые NVMe SSD; - Соответствие 152-ФЗ, PCI DSS; - Бесплатная защита от DDoS; - Управление через панель, API и Terraform; - Техподдержка 24/7. Запустите сервер за несколько минут! Попробовать #реклама 16+ selectel.ru О рекламодателе

Lock‑Free кэш на C11 atomics через Python – когда GIL не помогает, а блокировки давят В многопроцессной архитектуре (например, между воркерами Gunicorn или Celery) обычные Lock из threading или multiprocessing становятся узким местом из‑за контекстных переключений. Lock‑free структуры на атомарных операциях C11 обходят это. Python позволяет дотянуться до них через ctypes и _multiprocessing.sharedctypes, работая напрямую с разделяемой памятью без GIL. Частая ошибка: разработчики пишут свой lock‑free код, не учитывая memory ordering и ABA‑проблему. Как собирается атомарный кэш Берёшь RawValue и RawArray из _multiprocessing.sharedctypes, выделяешь разделяемую память для флагов, ключей и значений. Для синхронизации используешь C11 __sync_bool_compare_and_swap (CAS) через ctypes.CFUNCTYPE – он вызывает atomic-инструкцию на уровне процессора.
from ctypes import c_uint64, c_bool, CFUNCTYPE, POINTER, byref
from _multiprocessing.sharedctypes import RawValue, RawArray

class LockFreeCache:
    def __init__(self, capacity=256):
        self.capacity = capacity
        self.keys = RawArray(c_uint64, capacity)
        self.values = RawArray(c_uint64, capacity)
        self.flags = RawArray(c_bool, capacity)
        self._load_cas()

    def _load_cas(self):
        libc = ctypes.CDLL(None)
        self.cas = CFUNCTYPE(c_bool, POINTER(c_bool), c_bool, c_bool)(
            ('__sync_bool_compare_and_swap', libc)
        )

    def set(self, key, value):
        idx = hash(key) % self.capacity
        while True:
            old_flag = c_bool(False)
            if self.cas(byref(self.flags[idx]), old_flag, c_bool(True)):
                self.keys[idx] = key
                self.values[idx] = value
                self.flags[idx] = c_bool(False)
                return True
Production‑ориентированный пример Используй такой кэш на hot‑path, где простая блокичка через multiprocessing.Lock даёт ощутимый оверхед. Например, подсчёт запросов или кэширование результатов между воркерами в асинхронном HTTP‑обработчике. Для прода обязательно добавь управление коллизиями (хэш‑таблица с open addressing), TTL и видимость через memory fence (GCC __sync_synchronize). Trade‑offs и типичные ошибки Плюсы: без блокировок, масштабируется на многоядерных системах, GIL не мешает. Минусы: ABA‑проблема (например, чтение старой записи после перезаписи), платформозависимость (не на всех архитектурах есть CAS), риск data race при неправильном memory ordering. Предупреждение: не используй этот подход для сложных структур – lock‑free очередь или счётчик проще сделать через multiprocessing.Value с блокировкой. Вывод: Lock‑free кэш на атомарных операциях оправдан только на узком hot‑path, где блокировка реально давит – в остальных случаях простой Lock надёжнее и читаемее.

Новинка: «Инженерия данных. Паттерны проектирования» Издательство «O’Reilly» выпустило русское издание книги «Data Engineerin
Новинка: «Инженерия данных. Паттерны проектирования» Издательство «O’Reilly» выпустило русское издание книги «Data Engineering Design Patterns» Бартоша Конечны под названием «Инженерия данных. Паттерны проектирования». Книга вышла в конце июня 2026 года. Автор предлагает выделить в дисциплине инженерии данных универсальные шаблоны проектирования типичных решений, аналогичные тем, что описаны в книге «Design Patterns» «Банды четырёх» середины 1990-х. Ранее Бартош Конечны опубликовал статью-перевод, в которой обосновывал готовящуюся книгу и очерчивал её тематическое поле. Читать далее

About Python [ru] - Estadísticas y analítica del canal de Telegram @python_tesst