Библиотека Python разработчика | Книги по питону
Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍 По всем вопросам @evgenycarter РКН clck.ru/3Ko7Hq
Show more📈 Analytical overview of Telegram channel Библиотека Python разработчика | Книги по питону
Channel Библиотека Python разработчика | Книги по питону (@bookpython) in the Russian language segment is an active participant. Currently, the community unites 17 959 subscribers, ranking 7 250 in the Technologies & Applications category and 36 565 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 17 959 subscribers.
According to the latest data from 04 November, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -87 over the last 30 days and by -3 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 5.31%. Within the first 24 hours after publication, content typically collects 2.70% reactions from the total number of subscribers.
- Post reach: On average, each post receives 953 views. Within the first day, a publication typically gains 485 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 4.
- Thematic interests: Content is focused on key topics such as numbers, yield, модуль, none, декоратор.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Погружение в CPython и архитектуру. Разбираем неочевидное поведение (GIL, Memory), Best Practices (SOLID, DDD) и тонкости Django/FastAPI. Решаем задачи с подвохом и оптимизируем алгоритмы. 🐍
По всем вопросам @evgenycarter
РКН clck.ru/3Ko7Hq”
Thanks to the high frequency of updates (latest data received on 05 November, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
def edges(lst):
return lst[0], lst[-1]
first, last = edges([1, 2, 3])
assert first == 1
assert last == 3
In truth, lst[0], lst[-1] is a simple tuple. It's returned as usual and then unpacked to first and last:
result = edges([1, 2, 3])
assert isinstance(result, tuple)
first, last = result
assert first == 1
assert last == 3
Usually, you don't care about it at all. However, all these things come to the surface when you use type hints. You have to define the function return value as tuple:
def edges(lst) -> Tuple[int, int]:
return lst[0], lst[-1]
Calling that function is even harder. You may think that you can do something like this:
first: int, last: int = edges([1, 2, 3])
Or at least this:
first, last: Tuple[int, int] = edges([1, 2, 3])
But both ways are incorrect. This is the only reasonable thing you can do to annotate these variables:
first: int
last: int
first, last = edges([1, 2, 3])datetime. The interesting part is, datetime objects have the special interface for timezone support (namely the tzinfo attribute), but this module only has limited support of its interface, leaving the rest of the job to different modules.
The most popular module for this job is pytz. The tricky part is, pytz doesn't fully satisfy tzinfo interface. The pytz documentation states this at one of the first lines: “This library differs from the documented Python API for tzinfo implementations.”
You can't use pytz timezone objects as the tzinfo attribute. If you try, you may get the absolute insane results:
In : paris = pytz.timezone('Europe/Paris')
In : str(datetime(2017, 1, 1, tzinfo=paris))
Out: '2017-01-01 00:00:00+00:09'
Look at that +00:09 offset. The proper use of pytz is following:
In : str(paris.localize(datetime(2017, 1, 1)))
Out: '2017-01-01 00:00:00+01:00'
Also, after any arithmetic operations, you should normalize your datetime object in case of offset changes (on the edge of the DST period for instance).
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'
Since Python 3.6, it's recommended to use dateutil.tz instead of pytz. It's fully compatible with tzinfo, can be passed as an attribute, doesn't require normalize, though works a bit slower.
If you are interested why pytz doesn't support datetime API, or you wish to see more examples, consider reading the decent article on the topic.map function calls another function for every element of some iterable. That means that function should accept a single value as an argument:
In : list(map(lambda x: x ** 2, [1, 2, 3]))
Out: [1, 4, 9]
However, if each element of the iterable is tuple, then it would be nice to pass each element of that tuple as a separate argument. It was possible in Python 2, thanks to the tuple parameter unpacking (note the parentheses):
>>> map(lambda (a, b): a + b, [(1, 2), (3, 4)])
[3, 7]
In Python 3, this feature is gone, but there is another solution. itertools.starmap unpacks tuple for you, as though a function is called with a star: f(*arg) (hence the function's name):
In [3]: list(starmap(lambda a, b: a + b, [(1, 2), (3, 4)]))
Out[3]: [3, 7]locals()) is used by a metaclass (type by default) to construct an actual class object.
class Meta(type):
def __new__(meta, name, bases, ns):
print(ns)
return super().__new__(
meta, name,
bases, ns
)
class Foo(metaclass=Meta):
B = 2
The above code prints {'__module__': '__main__', '__qualname__': 'Foo', 'B': 3}.
Obviously, if you do something like B = 2; B = 3, then the metaclass only knows about B = 3, since only that value is in ns. This limitation is based on the fact, that a metaclass works after the body evaluation.
However, you can interfere in the evaluation by providing custom namespace. By default, a simple dictionary is used but you can provide a custom dictionary-like object using the metaclass __prepare__ method.
class CustomNamespace(dict):
def __setitem__(self, key, value):
print(f'{key} -> {value}')
return super().__setitem__(key, value)
class Meta(type):
def __new__(meta, name, bases, ns):
return super().__new__(
meta, name,
bases, ns
)
@classmethod
def __prepare__(metacls, cls, bases):
return CustomNamespace()
class Foo(metaclass=Meta):
B = 2
B = 3
The output is the following:
__module__ -> __main__
__qualname__ -> Foo
B -> 2
B -> 3
And this is how enum.Enum is protected from duplicates.Все уроки записаны в 2022 году, материал обновлен и дополнен.
Спикеры:
Марсель Ибраев (Southbridge)
Павел Селиванов (Yandex Cloud).
Форматы обучения:
Поток: открываем доступ к двум новым темам каждую неделю, общаемся в чате с куратором и спикерами, два раза в неделю — AMA-сессии по темам курса со спикерами.
Видеокурс: доступны сразу все темы, можно изучать в своём темпе.
Оба формата включают практику на стендах и итоговую сертификацию.
Подробнее про курс: https://slurm.club/3zPAuGrsuper() function allows referring to the base class. This can be extremely helpful in cases when a derived class wants to add something to the method implementation instead of overriding it completely:
class BaseTestCase(TestCase):
def setUp(self):
self._db = create_db()
class UserTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self._user = create_user()
The function's name doesn't mean excellent or very good. The word super implies above in this context (like in superintendant). Despite what I said earlier, super() doesn't always refer to the base class, it can easily return a sibling. The proper name could be next() since the next class according to MRO is returned.
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
# prints 'right'
print(Bottom().foo())
Mind that super() may produce different results since they depend on the MRO of the original call.
>>> Bottom().foo()
'right'
>>> Left().foo()
'top'gen.throw(e) you may raise an exception at the point where the gen generator is paused, i. e. at some yield. If gen catches the exception, get.throw(e) returns the next value yielded (or StopIteration is raised). If gen doesn't catch the exception, it propagates back to you.
In : def gen():
...: try:
...: yield 1
...: except ValueError:
...: yield 2
...:
...: g = gen()
...:
In : next(g)
Out: 1
In : g.throw(ValueError)
Out: 2
In : g.throw(RuntimeError('TEST'))
...
RuntimeError: TEST
You can use it to control generator behavior more precisely, not only by sending data to it but by notifying about some problems with values yielded for example. But this is rarely required, and you have a little chance to encounter g.throw in the wild.
However, the @contextmanager decorator from contextlib does exactly this to let the code inside the context catch exceptions.
In : from contextlib import contextmanager
...:
...: @contextmanager
...: def atomic():
...: print('BEGIN')
...:
...: try:
...: yield
...: except Exception:
...: print('ROLLBACK')
...: else:
...: print('COMMIT')
...:
In : with atomic():
...: print('ERROR')
...: raise RuntimeError()
...:
BEGIN
ERROR
ROLLBACK