Библиотека Python разработчика | Книги по питону
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq
显示更多📈 Telegram 频道 Библиотека Python разработчика | Книги по питону 的分析概览
频道 Библиотека Python разработчика | Книги по питону (@bookpython) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 17 970 名订阅者,在 技术与应用 类别中位列第 7 269,并在 俄罗斯 地区排名第 36 632 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 17 970 名订阅者。
根据 29 十月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -98,过去 24 小时变化为 -6,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 5.47%。内容发布后 24 小时内通常能获得 2.71% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 983 次浏览,首日通常累积 487 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 4。
- 主题关注点: 内容集中在 numbers, yield, модуль, none, декоратор 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍
По всем вопросам @evgenycarter
РКН clck.ru/3Ko7Hq”
凭借高频更新(最新数据采集于 30 十月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
+:
>>> [1, 2] + [2, 3]
[1, 2, 2, 3]
Кортежи и строки также используют +:
>>> (1, 2) + (2, 3)
(1, 2, 2, 3)
>>> "12" + "23"
'1223'
Deque (двусторонняя очередь) тоже поддерживает +:
>>> deque([1, 2]) + deque([2, 3])
deque([1, 2, 2, 3])
Множества объединяются с помощью оператора |:
>>> {1, 2} | {2, 3}
{1, 2, 3}
Словари объединяются по-другому, и порядок важен, если ключи пересекаются:
>>> {**dict(a=1, b=2), **dict(b=3, c=4)}
{'a': 1, 'b': 3, 'c': 4}
>>> {**dict(b=3, c=4), **dict(a=1, b=2)}
{'b': 2, 'c': 4, 'a': 1}
Counter (счётчик) можно сложить с помощью +, при этом значения суммируются:
>>> Counter(dict(a=1, b=2)) + Counter(dict(b=3, c=4))
Counter({'b': 5, 'c': 4, 'a': 1})
👉@BookPythonEllipsis, которую также можно записать как .... Эта константа не имеет особого значения для интерпретатора, но используется в тех местах, где подобный синтаксис уместен.
numpy поддерживает Ellipsis как аргумент для __getitem__. Например, x[...] возвращает все элементы массива x.
PEP 484 задаёт дополнительный смысл: Callable[..., type] — способ определить тип вызываемых объектов без указания типов аргументов.
Наконец, ... можно использовать, чтобы показать, что функция ещё не реализована. Это полностью корректный Python-код:
def x():
...
👉@BookPython
>>> class A:
... x = 2
... def f():
... print(x)
... f()
...
[...]
NameError: name 'x' is not defined
Обычно это не проблема: методы объявляются внутри класса только для того, чтобы стать методами и вызываться позже:
>>> class A:
... x = 2
... def f(self):
... print(self.x)
...
>>> A().f()
2
Что немного неожиданно — то же самое верно и для генераторов и списковых включений (comprehensions).
Они имеют свою собственную область видимости и не могут обращаться к области видимости класса.
Это особенно логично для генераторов, так как они выполняются уже после того, как создание класса завершено.
>>> class A:
... x = 2
... y = [x for _ in range(5)]
...
[...]
NameError: name 'x' is not defined
Comprehensions при этом не имеют доступа к self.
Единственный способ заставить это работать - добавить ещё один уровень области видимости, например, через lambda (да, это выглядит не слишком красиво):
>>> class A:
... x = 2
... y = (lambda x=x: [x for _ in range(5)])()
...
>>> A.y
[2, 2, 2, 2, 2]
👉@BookPythondict, хотя выглядят они абсолютно одинаково:
>>> from sys import getsizeof
>>> class A:
... pass
...
>>> a = dict()
>>> b = A().__dict__
>>> type(a)
<class 'dict'>
>>> type(b)
<class 'dict'>
>>> a
{}
>>> b
{}
>>> getsizeof(a)
240
>>> getsizeof(b)
112
Чтобы уменьшить потребление памяти, словари для __dict__ реализованы иначе. Они разделяют ключи между всеми экземплярами класса A. Однако важно понимать, что b на самом деле не меньше, чем a, - это просто особенность работы getsizeof.
👉@BookPythonrepr для других объектов внутри собственного метода __repr__ заключается в том, что нельзя гарантировать, что ни один из этих объектов не равен self, и вызов не окажется рекурсивным:
In : p = Pair(1, 2)
In : p
Out: Pair(1, 2)
In : p.right = p
In : p
Out: [...]
RecursionError: maximum recursion depth exceeded while calling a Python object
Чтобы легко решить эту проблему, можно использовать декоратор reprlib.recursive_repr:
@reprlib.recursive_repr()
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'
Теперь всё работает корректно:
In : p = Pair(1, 2)
In : p.right = p
In : p
Out: Pair(1, ...)
👉@BookPythontime.monotonic() никогда не идёт назад, даже если системные часы были изменены:
from contextlib import contextmanager
import time
@contextmanager
def timeit():
start = time.monotonic()
yield
print(time.monotonic() - start)
def main():
with timeit():
time.sleep(2)
main()
👉@BookPython@property:
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
Ниже приведён пример того, как можно определить функцию, которая использует дополнительные функции для особых случаев:
from functools import wraps
def make_case_decorator(func):
def case_decorator(*case_decorator_args):
def decorator(special_case_func):
@wraps(func)
def decorated(*args):
if case_decorator_args == args:
return special_case_func(*args)
return func(*args)
decorated.case = make_case_decorator(decorated)
return decorated
return decorator
return case_decorator
def special_cases(func):
@wraps(func)
def decorated(*args):
return func(*args)
decorated.case = make_case_decorator(decorated)
return decorated
@special_cases
def fact(x):
return x * fact(x - 1)
@fact.case(0)
def fact(x):
return 1
@fact.case(10)
def fact(x):
print(f'(сработала оптимизация для {x})')
return 3628800
👉@BookPython2:
def make_closure(x):
def closure():
print(x)
return closure
make_closure(2)()
Вызывает UnboundLocalError: local variable 'x' referenced before assignment:
def make_closure(x):
def closure():
print(x)
x *= 2
print(x)
return closure
make_closure(2)()
Чтобы это заработало, нужно использовать nonlocal.
Оно явно сообщает интерпретатору, что присваивание не создает новую локальную переменную, а работает с переменной из замыкания:
def make_closure(x):
def closure():
nonlocal x
print(x)
x *= 2
print(x)
return closure
make_closure(2)()
👉@BookPythontry ... except Exception, которая предназначена для отлова «любых ошибок». Чтобы безопасно это обработать внутри корутины, приходится писать примерно так:
try:
await action()
except asyncio.CancelledError:
raise
except Exception:
logging.exception('action failed')
👉@BookPythonwith внутри блока if, не заключив туда весь блок with. Это часто приводит к дублированию кода:
def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj
if path:
with open(path) as f:
print(f.read(), end='')
else:
print(file_obj.read(), end='')
Способ борьбы с этой проблемой — использовать ExitStack и вызывать enter_context внутри if:
def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj
with ExitStack() as stack:
if path:
file_obj = stack.enter_context(
open(path)
)
print(file_obj.read(), end='')
Однако более очевидный способ достичь того же — использовать тривиальные менеджеры контекста, которые ничего не делают, когда они не нужны, вместо «настоящих». Начиная с Python 3.7, их можно получить с помощью contextlib.nullcontext:
def print_whole_file(
*,
path: Optional[str] = None,
file_obj: Optional[TextIO] = None
):
assert path or file_obj
if path:
context = open(path)
else:
context = nullcontext(file_obj)
with context as f:
print(f.read(), end='')
👉@BookPythonitertools.tee() создаёт несколько итераторов из одного. Это может быть полезно, если нескольким потребителям нужно читать один и тот же поток.
Пример:
In : a, b, c = tee(iter(input, ''), 3)
In : next(a), next(c)
FIRST
Out: ('FIRST', 'FIRST')
In : next(a), next(b)
SECOND
Out: ('SECOND', 'FIRST')
In : next(a), next(b), next(c)
THIRD
Out: ('THIRD', 'SECOND', 'SECOND')
Данные, которые ещё не были использованы всеми итераторами, сохраняются в памяти. Если часть созданных итераторов ещё не была начата в тот момент, когда другой уже дошёл до конца, это означает, что все сгенерированные элементы будут храниться в памяти для будущего использования.
В таком случае проще и эффективнее использовать list(iter(input, '')), чем tee.
👉@BookPython