es
Feedback

¡No caigas en manos de tramposos! Telemetrio encuentra y marca estos canales 👉 Si quieres ver la etiqueta, suscríbete

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

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

Ir al canal en Telegram

Погружение в 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.

17 976
Suscriptores
-224 horas
-287 días
-9630 días
Archivo de publicaciones
Чтобы отсортировать словарь по его значениям, используйте функцию 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

Если вы создаёте новые объекты внутри метода __init__, возможно, будет лучше передавать их как аргументы и использовать фабричный метод. Это позволяет разделить бизнес-логику и технические детали создания объектов. В этом примере __init__ принимает host и port для создания подключения к базе данных:

class Query:
    def __init__(self, host, port):
        self._connection = Connection(host, port)
Возможный рефакторинг:

class Query:
    def __init__(self, connection):
        self._connection = connection
        
    @classmethod
    def create(cls, host, port):
        return cls(Connection(host, port))
Такой подход имеет как минимум следующие преимущества: • Упрощает внедрение зависимостей. В тестах можно использовать Query(FakeConnection()). • Класс может иметь столько фабричных методов, сколько нужно; подключение может создаваться не только по host и port, но и путём клонирования другого подключения, чтения конфигурационного файла или объекта, использования значения по умолчанию и т.д. • Такие фабричные методы можно сделать асинхронными; это невозможно для __init__. 📲 Мы в MAX 👉@BookPython

Чтобы сохранить любую информацию в памяти или на устройстве хранения, её необходимо представить в виде байтов. Python, как правило, предоставляет уровень абстракции, при котором вы оперируете непосредственно данными, а не их байтовым представлением. Тем не менее, когда вы, скажем, записываете строку в файл, вы работаете с физической структурой данных. Чтобы поместить символы в файл, их нужно преобразовать в байты — это называется кодированием. Когда вы получаете байты из файла, скорее всего, вы захотите превратить их в осмысленные символы — это называется декодированием. Существует сотни методов кодирования. Самый популярный — вероятно, Unicode, но он сам по себе не может преобразовывать данные в байты. В смысле байтового представления Unicode вообще не является кодировкой. Unicode определяет соответствие между символами и их числовыми кодами. Например, 🐍 имеет код 128013. Но чтобы записать числа в файл, нужна настоящая кодировка. Unicode обычно используется вместе с utf-8, которая (в большинстве случаев) является стандартной в Python. При чтении из файла Python автоматически декодирует utf-8. Вы можете выбрать любую другую кодировку с помощью параметра encoding= функции open, либо читать «сырые» байты, добавив b к режиму открытия файла. 📲 Мы в MAX 👉@BookPython

В Python очень короткий список встроенных констант. Одна из них — Ellipsis, которую также можно записать как .... Эта константа не имеет особого значения для интерпретатора, но используется в местах, где такой синтаксис выглядит уместно. Библиотека NumPy поддерживает Ellipsis в качестве аргумента __getitem__, например: x[...] возвращает все элементы массива x. PEP 484 придаёт Ellipsis дополнительное значение: Callable[..., type] — это способ определить тип вызываемых объектов без указания типов аргументов. Наконец, ... можно использовать, чтобы обозначить, что функция ещё не реализована. Это абсолютно валидный код на Python:

def x():
    ...
📲 Мы в MAX 👉@BookPython

Обработка исключений в асинхронных программах может быть непростой задачей. В asyncio, если корутина выбрасывает исключение, оно передаётся в тот код, который ожидает соответствующий future. Если await вызывается в нескольких местах, то каждое из них получит это исключение (так как оно сохраняется внутри объекта исключения). Следующий код напечатает сообщение об ошибке пять раз:

import asyncio

async def error():
    await asyncio.sleep(1)
    raise ValueError()

async def waiter(task):
    try:
        await task
    except ValueError:
        print('error')
    else:
        print('OK')

async def main():
    task = asyncio.get_event_loop().create_task(error())

    for _ in range(5):
        asyncio.get_event_loop().create_task(waiter(task))

    await asyncio.sleep(2)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Если исключение выброшено, но задача (task) ни разу не была ожидаема (awaited), исключение будет потеряно. В таком случае при уничтожении задачи вы получите предупреждение: “Task exception was never retrieved”. Когда вы используете await asyncio.gather(tasks) и одна из задач выбрасывает исключение, оно передаётся наружу. Однако если несколько задач выбросят исключения, вы получите только первое, остальные будут проигнорированы:

import asyncio

async def error(i):
    await asyncio.sleep(1)
    raise ValueError(i)

async def main():
    try:
        await asyncio.gather(
            error(1),
            error(2),
            error(3),
        )
    except ValueError as e:
        print(e)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Вы можете использовать gather с параметром return_exceptions=True, чтобы получать исключения как обычные значения. Следующий код напечатает: [42, ValueError(2,), ValueError(3,)]

import asyncio

async def error(i):
    await asyncio.sleep(1)
    if i > 1:
        raise ValueError(i)
    return 42

async def main():
    results = await asyncio.gather(
        error(1),
        error(2),
        error(3),
        return_exceptions=True,
    )

    print(results)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
📲 Мы в MAX 👉@BookPython