fa
Feedback
Библиотека Python разработчика | Книги по питону

Библиотека Python разработчика | Книги по питону

رفتن به کانال در Telegram

Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq

نمایش بیشتر

📈 تحلیل کانال تلگرام Библиотека Python разработчика | Книги по питону

کانال Библиотека Python разработчика | Книги по питону (@bookpython) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 18 328 مشترک است و جایگاه 7 299 را در دسته فناوری و برنامه‌ها و رتبه 36 904 را در منطقه روسيا دارد.

📊 شاخص‌های مخاطب و پویایی

از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 18 328 مشترک جذب کرده است.

بر اساس آخرین داده‌ها در تاریخ 03 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر -85 و در ۲۴ ساعت گذشته برابر -8 بوده و همچنان دسترسی گسترده‌ای حفظ شده است.

  • وضعیت تأیید: تأیید نشده
  • نرخ تعامل (ER): میانگین تعامل مخاطب 6.04% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 2.53% واکنش نسبت به کل مشترکان کسب می‌کند.
  • دسترسی پست‌ها: هر پست به طور میانگین 1 107 بازدید دریافت می‌کند. در اولین روز معمولاً 463 بازدید جمع‌آوری می‌شود.
  • واکنش‌ها و تعامل: مخاطبان به‌طور فعال حمایت می‌کنند؛ میانگین واکنش به هر پست 2 است.
  • علایق موضوعی: محتوا بر موضوعات کلیدی مانند numbers, yield, модуль, none, декоратор تمرکز دارد.

📝 توضیح و سیاست محتوایی

نویسنده این فضا را محل بیان دیدگاه‌های شخصی توصیف می‌کند:
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq

به لطف به‌روزرسانی‌های پرتکرار (آخرین داده در تاریخ 04 ژوئن, 2026)، کانال همواره به‌روز و دارای دسترسی بالاست. تحلیل‌ها نشان می‌دهد مخاطبان به‌طور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامه‌ها تبدیل کرده‌اند.

18 328
مشترکین
-824 ساعت
-357 روز
-8530 روز
آرشیو پست ها
Создание объекта в Python включает два ключевых этапа. Сначала вызывается метод __new__, который создаёт и возвращает новый объект. Затем вызывается метод __init__ для инициализации состояния этого объекта. Однако, если __new__ возвращает объект, который не является экземпляром исходного класса, метод __init__ не будет вызван. Это связано с тем, что возвращаемый объект, вероятно, уже создан другим классом, и его __init__ уже был выполнен:

class Foo:
    def __new__(cls, x):
        return dict(x=x)

    def __init__(self, x):
        print(x)  # Никогда не вызывается

print(Foo(0))
Важно: не следует создавать экземпляры того же класса в __new__ с использованием обычного конструктора (Foo(...)). Это может привести к двойному вызову __init__ или даже к бесконечной рекурсии. Пример бесконечной рекурсии:

class Foo:
    def __new__(cls, x):
        return Foo(-x)  # Рекурсия
Пример двойного вызова __init__:

class Foo:
    def __new__(cls, x):
        if x < 0:
            return Foo(-x)
        return super().__new__(cls)

    def __init__(self, x):
        print(x)
        self._x = x
Правильный способ:

class Foo:
    def __new__(cls, x):
        if x < 0:
            return cls.__new__(cls, -x)
        return super().__new__(cls)

    def __init__(self, x):
        print(x)
        self._x = x
📲 Мы в MAX 👉@BookPython

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

Как упростить работу с аргументами в командной строке с помощью typer Раньше для CLI-приложений на Python я использовал argparse, потом был click, но недавно полностью перешёл на typer. Это библиотека от автора FastAPI, и она реально 🔥 Вот простой пример:

import typer

app = typer.Typer()

@app.command()
def hello(name: str, age: int = 18):
    print(f"Привет, {name}! Тебе {age} лет.")

if __name__ == "__main__":
    app()
Теперь можно запускать в терминале:

$ python main.py hello Alice --age 30
Привет, Alice! Тебе 30 лет.
Что круто: - Автоматически генерируется --help - Пишется почти как обычная функция - Есть автокомплит в оболочках (bash/zsh) - Поддержка аннотаций типов и валидации "из коробки" Если ты всё ещё страдаешь с argparse, рекомендую попробовать typer. Особенно если ты уже кайфуешь от FastAPI — синтаксис и подход очень похожи. 📲 Мы в MAX 👉@BookPython

Популярный способ объявить абстрактный метод в Python — это выбросить исключение NotImplementedError:

def human_name(self):
    raise NotImplementedError
Хотя этот подход довольно распространён и даже поддерживается IDE (например, PyCharm считает такие методы абстрактными), у него есть недостаток: ошибка возникает только при вызове метода, а не при создании экземпляра класса. Чтобы избежать этой проблемы, используйте модуль abc:

from abc import ABCMeta, abstractmethod

class Service(metaclass=ABCMeta):
    @abstractmethod
    def human_name(self):
        pass
Также важно помнить, что NotImplemented — это не то же самое, что NotImplementedError. NotImplemented — это специальное значение (как True и False), а не исключение. Оно используется, например, в специальных методах (__eq__(), __add__() и др.), чтобы сообщить Python, что операция не реализована для данного типа, и попытаться вызвать альтернативный метод (например, если a.__add__(b) возвращает NotImplemented, Python попробует вызвать b.__radd__(a)). 📲 Мы в MAX 👉@BookPython

Чтобы отсортировать словарь по его значениям, используйте функцию sorted с пользовательской функцией ключа:

>>> d = dict(a=1, c=3, b=2)
>>> sorted(d.items(), key=lambda item: item[1])
[('a', 1), ('b', 2), ('c', 3)]
Однако такая функция уже существует в модуле operator:

>>> from operator import itemgetter
>>> sorted(d.items(), key=itemgetter(1))
[('a', 1), ('b', 2), ('c', 3)]
Вы также можете сортировать только ключи вместо пар ключ-значение:

>>> sorted(d, key=lambda k: d[k])
['a', 'b', 'c']
И снова, эту лямбду можно заменить уже существующим методом:

>>> sorted(d, key=d.get)
['a', 'b', 'c']
📲 Мы в MAX 👉@BookPython

Некоторый код может выводить интересующие вас данные в stdout, вместо того чтобы предоставлять API, возвращающий строку, пригодную для использования в программе. Вместо рефакторинга такого кода можно воспользоваться менеджером контекста contextlib.redirect_stdout, который позволяет временно перенаправить stdout в любой объект, поддерживающий файловый интерфейс. В сочетании с io.StringIO это позволяет сохранить вывод в переменную.

from contextlib import redirect_stdout
from io import StringIO

s = StringIO()
with redirect_stdout(s):
    print(42)

print(s.getvalue())
Также существует contextlib.redirect_stderr для перенаправления вывода sys.stderr. 📲 Мы в MAX 👉@BookPython

Иногда в программе нужна очередь — контейнер, куда элементы добавляются с одной стороны и извлекаются с другой. В Python для этого можно использовать list:

In : lst = [1, 2, 3]
In : lst.pop()
Out: 3
In : lst
Out: [1, 2]
In : lst[:0] = [4]  # push
In : lst
Out: [4, 1, 2]
Однако list выглядит не очень удобно (взгляните на этот "push") и работает неэффективно.

In : lst = [0] * 10_000_000

In : %timeit lst[:0] = [1]
9.5 ms ± 111 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In : %timeit lst.pop()
84.3 ns ± 4.01 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Как видно, операция pop() в 100 раз быстрее, чем вставка в начало списка. Это связано с тем, как устроен list в Python: легко добавлять и удалять элементы с конца, но удаление/добавление в начало требует создания нового списка. Для очередей лучше использовать collections.deque. Он специально для этого создан:

In : from collections import deque
In : d = deque([1] * 100_000_000)
In : %timeit d.popleft()
65 ns ± 0.436 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
📲 Мы в MAX 👉@BookPython

Скрытые фичи Enum: как выжать максимум Многие используют Enum как простой список констант. Но у enum.Enum в Python есть куда больше возможностей — и они могут сделать код чище и мощнее. Вот несколько приёмов, которые мало кто использует — но зря. 1. Добавление поведения в Enum

from enum import Enum

class Status(Enum):
    DRAFT = 'draft'
    PUBLISHED = 'published'
    ARCHIVED = 'archived'

    def is_visible(self):
        return self in {Status.DRAFT, Status.PUBLISHED}
Теперь Status.DRAFT.is_visible() — это просто и элегантно. 2. Enum с полями

from enum import Enum

class Color(Enum):
    RED = ('#FF0000', 'danger')
    GREEN = ('#00FF00', 'safe')

    def __init__(self, hex_code, label):
        self.hex_code = hex_code
        self.label = label

Color.RED.hex_code  # '#FF0000'
Color.RED.label     # 'danger'
3. Автоматические значения с auto()

from enum import Enum, auto

class Role(Enum):
    ADMIN = auto()
    USER = auto()
    GUEST = auto()
Удобно, если не важны конкретные значения, а нужны уникальные. 4. Строгая сериализация В реальных приложениях (API, базы) лучше контролировать сериализацию enum'ов:

import json

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Enum):
            return obj.value
        return super().default(obj)

json.dumps(Status.PUBLISHED, cls=CustomEncoder)  # "published"
5. Сравнение по значению

Status('draft') == Status.DRAFT  # True
Status('draft') is Status.DRAFT  # True (enum гарантирует singleton)
Итого: Enum — это не просто константы. Это лёгкий способ инкапсулировать поведение и данные, улучшить читаемость и сделать код устойчивее к ошибкам. 📲 Мы в MAX 👉@BookPython

Стандартный модуль json имеет интерфейс командной строки, который может быть полезен для форматирования JSON исключительно средствами Python. Модуль называется json.tool и вызывается следующим образом:

$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
    "a": [],
    "b": "c"
}
📲 Мы в MAX 👉@BookPython

default_factory в dataclass: мощнее, чем кажется Многие используют dataclass как удобный способ задать структуру с полями. Но редко кто по-настоящему раскрывает силу default_factory. А зря — он спасает от багов и даёт гибкость. Когда нужно задать значение по умолчанию для поля в dataclass, логично тянуться к default=. Но если это изменяемый тип (например, список или словарь) — вас поджидает ловушка.

from dataclasses import dataclass, field

@dataclass
class User:
    name: str
    tags: list[str] = []  # ⚠️ опасно!
Все экземпляры User будут делить один и тот же список. То есть:

a = User("Alice")
b = User("Bob")
a.tags.append("admin")

print(b.tags)  # ['admin'] 😱
Вместо этого используйте default_factory:

@dataclass
class User:
    name: str
    tags: list[str] = field(default_factory=list)
Теперь у каждого User будет свой список:

a = User("Alice")
b = User("Bob")
a.tags.append("admin")

print(b.tags)  # []
Но default_factory не только про списки. Это отличный способ задать любое значение "по умолчанию", включая кастомную логику:

import uuid

@dataclass
class Session:
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
Или, например, значения из окружения:

import os

@dataclass
class Config:
    debug: bool = field(default_factory=lambda: os.getenv("DEBUG") == "1")
Кстати, это ещё и отличное место для внедрения DI:

@dataclass
class Service:
    client: "Client" = field(default_factory=create_default_client)
default_factory — это маленький хак, который позволяет сделать код чище и безопаснее, особенно когда работаешь с изменяемыми структурами или сложной инициализацией. 📲 Мы в MAX 👉@BookPython

yield from — элегантная передача управления Если вы пишете генераторы, которые вызывают другие генераторы — забудьте про for x in sub(): yield x. Есть способ проще и мощнее. Оператор yield from позволяет передавать элементы из подгенератора напрямую, без лишнего кода. Но фишка не только в лаконичности — он также автоматически пробрасывает исключения и возвращаемые значения из подгенератора. Вот классика:

def gen():
    for x in range(3):
        yield x

def wrapper():
    for x in gen():
        yield x
Можно короче и лучше:

def wrapper():
    yield from gen()
Но главное — yield from пробрасывает return-значение из подгенератора (начиная с Python 3.3):

def sub():
    yield 1
    yield 2
    return 'done'

def main():
    result = yield from sub()
    print('Sub returned:', result)

for _ in main():
    pass
# Выведет: Sub returned: done
А ещё через yield from можно проксировать значения внутрь генератора — например, в сопрограммах:

def delegator():
    result = yield from coroutine()
    print('coroutine done:', result)

def coroutine():
    x = yield
    y = yield
    return x + y

g = delegator()
next(g)          # Старт
next(g)          # coroutine ждет x
g.send(10)       # x = 10
print(g.send(20))  # y = 20 → return 30
# Выведет: coroutine done: 30
Итог: если вы пишете генераторы — освоение yield from даст вам лаконичный синтаксис, проброс return-значений, исключений и взаимодействие на новом уровне. 📲 Мы в MAX 👉@BookPython

🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии 📌 https://max.ru/progjob Вакансии в IT Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы

Python предоставляет мощную библиотеку для работы с датой и временем — datetime. Интересный момент: объекты datetime имеют специальный интерфейс для поддержки часовых поясов (атрибут tzinfo), однако сама библиотека `datetime реализует его лишь частично, оставляя остальную работу сторонним модулям. Самым популярным модулем для этой задачи является pytz. Хитрость в том, что pytz не полностью соответствует интерфейсу tzinfo. В документации pytz прямо указано с самого начала: «Эта библиотека отличается от задокументированного API Python для реализаций tzinfo». Вы не можете просто передать объект временной зоны pytz в атрибут tzinfo. Если попробуете, результат может быть абсолютно безумным:

In : paris = pytz.timezone('Europe/Paris')
In : str(datetime(2017, 1, 1, tzinfo=paris))
Out: '2017-01-01 00:00:00+00:09'
Посмотрите на этот смещение +00:09. Правильное использование pytz выглядит так:

In : str(paris.localize(datetime(2017, 1, 1)))
Out: '2017-01-01 00:00:00+01:00'
Кроме того, после любых операций с датой и временем, нужно нормализовать объект datetime, если есть вероятность смены смещения (например, на границе перехода на летнее время):

In : new_time = time + timedelta(days=2)
In : str(new_time)
Out: '2018-03-27 00:00:00+01:00'
In : str(paris.normalize(new_time))
Out: '2018-03-27 01:00:00+02:00'
Начиная с Python 3.6, рекомендуется использовать dateutil.tz вместо pytz. Он полностью совместим с tzinfo, может использоваться напрямую, не требует normalize, хотя и работает немного медленнее. Если вам интересно, почему pytz не поддерживает API datetime, или вы хотите увидеть больше примеров, обязательно почитайте хорошую статью на эту тему. 📲 Мы в MAX 👉@BookPython

Когда вы используете модуль multiprocessing, и в одном из процессов происходит исключение, оно передаётся в основную программу с помощью механизма сериализации (pickling). Исключение сериализуется, передаётся в другой процесс и там десериализуется обратно. Однако сериализация исключений может быть непростой задачей. Исключение создаётся с любым количеством аргументов, которые сохраняются в атрибуте args. Эти же аргументы используются при десериализации для воссоздания объекта исключения. Но это может не сработать так, как вы ожидаете, особенно если используется наследование. Посмотрите на пример:

import pickle

class TooMuchWeightError(Exception):
    def __init__(self, weight):
        super().__init__()
        self._weight = weight

pickled = pickle.dumps(TooMuchWeightError(42))
pickle.loads(pickled)
Вызов TooMuchWeightError.__init__ приводит к вызову Exception.__init__, который устанавливает args как пустой кортеж. Этот пустой кортеж затем используется в качестве аргументов при десериализации, что, очевидно, приводит к ошибке:
TypeError: __init__() missing 1 required positional argument: 'weight'
Обходное решение — либо вообще не вызывать super().__init__() (что обычно считается плохой практикой при наследовании), либо передавать все аргументы явно в конструктор родительского класса:

class TooMuchWeightError(Exception):
    def __init__(self, weight):
        super().__init__(weight)
        self._weight = weight
📲 Мы в MAX 👉@BookPython

Функция map вызывает другую функцию для каждого элемента итерируемого объекта. Это значит, что функция должна принимать одно значение в качестве аргумента:

In : list(map(lambda x: x ** 2, [1, 2, 3]))
Out: [1, 4, 9]
Однако если каждый элемент итерируемого объекта — это кортеж, было бы удобно передавать каждый элемент кортежа как отдельный аргумент. В Python 2 это было возможно благодаря распаковке параметров кортежа (обратите внимание на скобки):

>>> map(lambda (a, b): a + b, [(1, 2), (3, 4)])
[3, 7]
В Python 3 эта возможность исчезла, но есть другое решение — itertools.starmap. Она распаковывает кортежи за вас, будто функция вызывается со звёздочкой: f(*arg) (отсюда и название функции):

from itertools import starmap

In [3]: list(starmap(lambda a, b: a + b, [(1, 2), (3, 4)]))
Out[3]: [3, 7]
📲 Мы в MAX 👉@BookPython

Функция super() позволяет обращаться к родительскому (базовому) классу. Это может быть очень полезно в случаях, когда производный класс хочет добавить что-то к реализации метода, а не полностью переопределять его:

class BaseTestCase(TestCase):
    def setUp(self):
        self._db = create_db()

class UserTestCase(BaseTestCase):
    def setUp(self):
        super().setUp()
        self._user = create_user()
Имя функции super не означает "отличный" или "очень хороший". В данном контексте слово super означает "выше" (как, например, в слове superintendent — заведующий). Несмотря на это, super() не всегда ссылается на базовый класс — он может вернуть и "соседний" класс. Более точным названием была бы, возможно, функция next(), так как возвращается следующий класс согласно цепочке разрешения методов (MRO — Method Resolution Order). Пример:

class Top:
    def foo(self):
        return 'top'

class Left(Top):
    def foo(self):
        return super().foo()

class Right(Top):
    def foo(self):
        return 'right'

class Bottom(Left, Right):
    pass

# выводит 'right'
print(Bottom().foo())
Обрати внимание: результат работы super() может отличаться в зависимости от MRO вызвавшего объекта.

>>> Bottom().foo()
'right'
>>> Left().foo()
'top'
📲 Мы в MAX 👉@BookPython

Стандартный механизм расширения путей в оболочке называется globbing. Шаблоны, которые вы используете для сопоставления путей
Стандартный механизм расширения путей в оболочке называется globbing. Шаблоны, которые вы используете для сопоставления путей, называются globs.

$ echo /li*
/lib /lib64
Python поддерживает globbing с помощью модуля glob. Однако есть важное замечание: оболочка возвращает сам шаблон, если файлы не найдены, а Python — нет:

$ echo /zz**
/zz**
$ python -c 'from glob import glob; print(glob("/zz**"))'
[]
📲 Мы в MAX 👉@BookPython

Обычно вы взаимодействуете с генератором, запрашивая данные с помощью next(gen). В Python 3 вы также можете отправлять значения обратно в генератор с помощью g.send(x). Но существует техника, которой вы, вероятно, не пользуетесь каждый день, а возможно, и вовсе не знаете: выбрасывание исключений внутри генератора. С помощью gen.throw(e) можно выбросить исключение в той точке, где генератор gen приостановлен — то есть на инструкции yield. Если генератор обрабатывает это исключение, gen.throw(e) возвращает следующее значение, полученное через yield (или выбрасывает StopIteration, если генератор завершён). Если генератор не перехватывает исключение, оно пробрасывается обратно к вызывающему коду.

def gen():
    try:
        yield 1
    except ValueError:
        yield 2

g = gen()

next(g)
# Out: 1

g.throw(ValueError)
# Out: 2

g.throw(RuntimeError('TEST'))
# RuntimeError: TEST
Эта техника позволяет более точно управлять поведением генератора — не только передавать данные внутрь, но и, например, сообщать о проблемах со значениями, полученными через yield. Однако такие случаи бывают редко, и встретить g.throw в дикой природе почти невозможно. Тем не менее, декоратор @contextmanager из модуля contextlib использует именно такую технику, позволяя коду внутри контекста перехватывать исключения.

from contextlib import contextmanager

@contextmanager
def atomic():
    print('BEGIN')

    try:
        yield
    except Exception:
        print('ROLLBACK')
    else:
        print('COMMIT')

with atomic():
    print('ERROR')
    raise RuntimeError()

BEGIN  
ERROR  
ROLLBACK
📲 Мы в MAX 👉@BookPython

Одна и та же строка может быть представлена по-разному в Unicode, и стандарт это учитывает. Он определяет два типа эквивалентности: последовательности могут быть канонически эквивалентными или совместимыми. Канонически эквивалентные последовательности выглядят одинаково, но содержат разные кодовые точки. Например, символ ö может быть представлен как LATIN SMALL LETTER O WITH DIAERESIS (U+00F6) или как комбинация из o и диакритического знака: LATIN SMALL LETTER O (U+006F) + COMBINING DIAERESIS (U+0308). Совместимые последовательности выглядят по-разному, но могут трактоваться одинаково с точки зрения смысла, например, ff и ff. Для каждого из этих типов эквивалентности можно нормализовать строку в Unicode, сжимая или расширяя последовательности. В Python для этого используется модуль unicodedata:

import unicodedata

modes = [
    # Сжать канонически эквивалентные
    'NFC',
    # Расширить канонически эквивалентные
    'NFD',
    # Сжать совместимые
    'NFKC',
    # Расширить совместимые
    'NFKD',
]

s = 'ff + ö'

for mode in modes:
    norm = unicodedata.normalize(mode, s)
    print('\t'.join([
        mode,
        norm,
        str(len(norm.encode('utf8'))),
    ]))
Результат:
NFC     ff + ö   8
NFD     ff + ö   9
NFKC    ff + ö  7
NFKD    ff + ö  8
📲 Мы в MAX 👉@BookPython

Сегодня я покажу вам простой, но очень полезный приём, который часто выручает при работе с Python-скриптами — автоматическое логирование вызовов функций с помощью декоратора. Иногда, особенно в отладке, хочется видеть, какие функции вызываются, с какими аргументами и что они возвращают. Не писать же в каждую вручную print()? Вот тут и приходит на помощь наш герой — универсальный логгер-декоратор:

import functools

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[CALL] {func.__name__} args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"[RETURN] {func.__name__} -> {result}")
        return result
    return wrapper
Пример использования:

@log_calls
def multiply(a, b):
    return a * b

multiply(3, 5)
📌 Вывод:
[CALL] multiply args=(3, 5) kwargs={}
[RETURN] multiply -> 15
Такой декоратор можно подключить временно на любую функцию — и сразу видеть, что происходит у вас в коде. Это особенно удобно при работе со сторонними библиотеками или когда вы разбираетесь в чужом проекте. Кстати, с небольшими изменениями можно направить вывод не в print(), а в logging, или даже сохранять в файл — по вкусу. Пользуетесь такими декораторами? Или у вас свой лайфхак? 📲 Мы в MAX 👉@BookPython