Библиотека Python разработчика | Книги по питону
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq
Mostrar más📈 Análisis del canal de Telegram Библиотека Python разработчика | Книги по питону
El canal Библиотека Python разработчика | Книги по питону (@bookpython) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 17 976 suscriptores, ocupando la posición 7 272 en la categoría Tecnologías y Aplicaciones y el puesto 36 658 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 17 976 suscriptores.
Según los últimos datos del 28 octubre, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -96, y en las últimas 24 horas de -2, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 5.41%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 2.64% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 973 visualizaciones. En el primer día suele acumular 475 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 4.
- Intereses temáticos: El contenido se centra en temas clave como numbers, yield, модуль, none, декоратор.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍
По всем вопросам @evgenycarter
РКН clck.ru/3Ko7Hq”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 29 octubre, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
IndexError, и KeyError, вы можете и должны использовать LookupError — их общего предка. Это оказывается полезным при работе со сложными вложенными данными:
try:
db_host = config['databases'][0]['hosts'][0]
except LookupError:
db_host = 'localhost'
📲 Мы в MAX
👉@BookPython
class A: pass
a = A()
a.x = 1
a.__dict__
# {'x': 1}
При прямом доступе к этому словарю можно даже создать атрибуты, которые не являются допустимыми идентификаторами Python (то есть получить их через стандартный синтаксис obj.attr не получится):
a.__dict__[' '] = ' '
getattr(a, ' ')
# ' '
Можно также попросить Python хранить атрибуты непосредственно в памяти (как у простых структур C) с помощью slots. Это экономит память и немного ускоряет доступ к атрибутам, так как не происходит поиска в словаре.
class Point:
__slots__ = ['x', 'y']
Есть несколько моментов, которые нужно помнить при использовании slots. Во-первых, нельзя задавать атрибуты, не указанные в slots. Во-вторых, если класс наследуется от класса с slots, его slots не перекрывают родительские, а добавляются к ним:
class Parent: __slots__ = ['x']
class Child(Parent): __slots__ = ['y']
c = Child()
c.x = 1
c.y = 2
В-третьих, нельзя наследоваться сразу от двух разных классов с непустыми slots, даже если они совпадают.
Помни, что slots предназначены для оптимизации, а не для ограничения набора атрибутов.
📲 Мы в MAX
👉@BookPythonos.path. Модуль содержит множество функций, которые обрабатывают строки как пути и выполняют полезные операции, такие как объединение путей и прочее:
>>> import os.path
>>> os.path.join('/usr', 'local')
'/usr/local'
>>> os.path.dirname('/var/log')
'/var'
Однако, начиная с Python 3.4, доступен новый модуль pathlib, предлагающий объектно-ориентированный подход:
>>> from pathlib import Path
>>> Path('/usr') / Path('local')
PosixPath('/usr/local')
>>> Path('/usr') / 'local'
PosixPath('/usr/local')
>>> Path('/var/log').parent
PosixPath('/var')
>>> Path('/var/log').parent.name
'var'
📲 Мы в MAX
👉@BookPython
>>> def enclose(gen, before='{', after='}'):
... yield before
... for x in gen:
... yield x
... yield after
...
>>> list(enclose(range(5)))
['{', 0, 1, 2, 3, 4, '}']
Однако предпочтительнее использовать yield from:
>>> def enclose(gen, before='{', after='}'):
... yield before
... yield from gen
... yield after
yield from не только работает быстрее, но и автоматически обрабатывает передачу значений во вложенные генераторы, возврат значений из генераторов и даже выброс исключений внутри вложенного генератора.
📲 Мы в MAX
👉@BookPythoncollections предоставляет класс ChainMap, который позволяет использовать несколько отображений (словарей) как одно объединённое:
from collections import ChainMap
d = ChainMap(dict(a=1), dict(a=2, b=2))
d['a'] # 1
d['b'] # 2
d['c'] # ...
# KeyError: 'c'
ChainMap последовательно просматривает все вложенные отображения и возвращает первое найденное значение. Однако все операции изменения затрагивают только первое отображение:
d = ChainMap(dict(a=1), dict(a=2, b=2))
d['c'] = 3
d
# ChainMap({'a': 1, 'c': 3}, {'a': 2, 'b': 2})
📲 Мы в MAX
👉@BookPython
a = [2, -1, 0, 1, -2]
sorted(a, key=lambda x: x**2)
# [0, -1, 1, 2, -2]
Функции max и min тоже стараются быть согласованными с поведением sorted.
max работает аналогично sorted(a, reverse=True)[0], а min — как sorted(a)[0].
Это означает, что обе функции возвращают самый левый возможный результат:
max([2, -2], key=lambda x: x**2)
# 2
max([-2, 2], key=lambda x: x**2)
# -2
min([2, -2], key=lambda x: x**2)
# 2
min([-2, 2], key=lambda x: x**2)
# -2
📲 Мы в MAX
👉@BookPythonfor и инструкции with.
Все эти примеры некорректны:
name: str, age: int = student
for x: int in numbers:
...
with connection() as conn: Connection:
...
Правильный способ указать тип таких переменных — объявить их заранее, без инициализации:
conn: Connection
with connection() as conn:
...
📲 Мы в MAX
👉@BookPython
>>> 2 + 2
4
>>> _
4
Во-вторых, в документации модуля gettext рекомендуется создавать псевдоним для функции gettext() в виде _(), чтобы не загромождать код.
В-третьих, _ используется, когда необходимо придумать имя для значения, которое не представляет интереса:
>>> log_entry = '10:50:24 14234 GET /api/v1/test'
>>> time, _, method, location = log_entry.split()
📲 Мы в MAX
👉@BookPythonexcept Exception, а не голый except:
try:
foreign()
except Exception:
logging.warn('fail', exc_info=True)
Голый except эквивалентен except BaseException. А разница между BaseException и Exception в том, что BaseException включает исключения, которые, как правило, ловить не следует, например, KeyboardInterrupt.
📲 Мы в MAX
👉@BookPython[] можно переопределить, реализовав магический метод __getitem__. Это позволяет, например, создать объект, который виртуально содержит бесконечное количество повторяющихся элементов:
class Cycle:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, index):
return self._lst[index % len(self._lst)]
print(Cycle(['a', 'b', 'c'])[100]) # 'b'
Необычность оператора [] в Python в том, что он поддерживает особый синтаксис. Его можно использовать не только так: [2], но и так: [2:10], [2:10:2], [2::2] или даже [:]. Смысл такой записи — [start:stop:step], но в ваших собственных объектах вы можете использовать этот синтаксис как угодно.
Что же передаётся в __getitem__ в таких случаях? Объекты slice созданы специально для этого.
Пример:
class Inspector:
def __getitem__(self, index):
print(index)
Inspector()[1]
# 1
Inspector()[1:2]
# slice(1, 2, None)
Inspector()[1:2:3]
# slice(1, 2, 3)
Inspector()[:]
# slice(None, None, None)
Inspector()[:, 0, :]
# (slice(None, None, None), 0, slice(None, None, None))
Объект slice сам по себе ничего не делает — он просто хранит атрибуты start, stop и step:
s = slice(1, 2, 3)
print(s.start) # 1
print(s.stop) # 2
print(s.step) # 3
📲 Мы в MAX
👉@BookPythonoverride), в других — это необязательно (в Java можно, но не обязательно использовать аннотацию @Override). В Python нет ни обязательного, ни стандартного способа обозначать такие методы (некоторые программисты применяют пользовательский декоратор @override, который ничего не делает, а служит только для читаемости кода).
Перегрузка, напротив, — это наличие нескольких функций с одним и тем же именем, но разными сигнатурами. Это поддерживается в таких языках, как Java и C++, и часто используется как способ предоставления значений по умолчанию:
class Foo {
public static void main(String[] args) {
System.out.println(Hello());
}
public static String Hello() {
return Hello("world");
}
public static String Hello(String name) {
return "Hello, " + name;
}
}
Python не поддерживает поиск функций по сигнатурам, только по именам. Вы можете написать код, который явно анализирует типы и количество аргументов, но это обычно выглядит громоздко и не очень красиво:
def quadrilateral_area(*args):
if len(args) == 4:
quadrilateral = Quadrilateral(*args)
elif len(args) == 1:
quadrilateral = args[0]
else:
raise TypeError()
return quadrilateral.area()
Если вам нужны подсказки типов для такой реализации, модуль typing предоставляет декоратор @overload, который можно использовать следующим образом:
from typing import overload
@overload
def quadrilateral_area(
q: Quadrilateral
) -> float: ...
@overload
def quadrilateral_area(
p1: Point, p2: Point,
p3: Point, p4: Point
) -> float: ...
📲 Мы в MAX
👉@BookPythonprint(), либо подключают logging, но каждый раз пишут кучу однотипного кода. Я так тоже делал. Но потом вывел себе простую универсальную схему, которую теперь кидаю в каждый новый проект:
import logging
def setup_logger(name: str) -> logging.Logger:
logger = logging.getLogger(name)
if not logger.hasHandlers():
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(name)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = setup_logger(__name__)
logger.info("Скрипт стартовал")
Что мы получаем:
* Удобный формат времени и уровня лога
* Защиту от дублирования логов (если модуль импортируется несколько раз)
* Готовность к масштабированию (можно легко добавить файл-логгер)
Если вы устали от print(), просто сохраните себе этот сниппет — он сэкономит вам время и нервы.
Пользуетесь ли вы встроенным logging, или предпочитаете что-то вроде loguru?
📲 Мы в MAX
👉@BookPythonfunctools.partial
Как упростить код и избежать дублирования с помощью functools.partial.
Допустим, у нас есть функция send_email(to, subject, body, is_html=False), и мы часто вызываем её с одним и тем же параметром is_html=True.
Вместо того чтобы каждый раз писать это явно, можно создать частичную функцию:
from functools import partial
send_html_email = partial(send_email, is_html=True)
# Теперь можно вызывать проще:
send_html_email("user@example.com", "Привет", "<b>Как дела?</b>")
Это удобно, если вы хотите предварительно зафиксировать часть аргументов, например:
* логгеры с предустановленным уровнем
* коннекторы с общими параметрами
* команды CLI с типовыми флагами
Таким образом, вы уменьшаете дублирование и делаете код читаемее. А ещё это красивый способ внедрить DI без фреймворков — просто передайте partial.
📲 Мы в MAX
👉@BookPythonfloat в Python используют оборудование вашего компьютера напрямую, поэтому любое значение представляется внутренне в виде двоичной дроби.
Это означает, что вы обычно работаете с приближениями, а не с точными значениями:
>>> format(0.1, '.17f')
'0.10000000000000001'
Модуль decimal позволяет использовать десятичную арифметику с произвольной точностью:
>>> from decimal import Decimal
>>> Decimal(1) / Decimal(3)
Decimal('0.3333333333333333333333333333')
Но и этого может быть недостаточно:
>>> Decimal(1) / Decimal(3) * Decimal(3) == Decimal(1)
False
Для абсолютно точных вычислений можно использовать модуль fractions, который хранит любое число как рациональное:
>>> from fractions import Fraction
>>> Fraction(1) / Fraction(3) * Fraction(3) == Fraction(1)
True
Очевидное ограничение — всё равно приходится использовать приближения для иррациональных чисел, таких как π.
📲 Мы в MAX
👉@BookPython__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
👉@BookPythontyper
Раньше для 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
👉@BookPythonNotImplementedError:
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