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-значений, исключений и взаимодействие на новом уровне.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
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 — это маленький хак, который позволяет сделать код чище и безопаснее, особенно когда работаешь с изменяемыми структурами или сложной инициализацией.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
🫡4❤1👍1
Стандартный модуль
📲 Мы в MAX
👉@BookPython
json имеет интерфейс командной строки, который может быть полезен для форматирования JSON исключительно средствами Python. Модуль называется json.tool и вызывается следующим образом:
$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
"a": [],
"b": "c"
}
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Скрытые фичи
Многие используют
Вот несколько приёмов, которые мало кто использует — но зря.
1. Добавление поведения в Enum
Теперь
2. Enum с полями
3. Автоматические значения с
Удобно, если не важны конкретные значения, а нужны уникальные.
4. Строгая сериализация
В реальных приложениях (API, базы) лучше контролировать сериализацию enum'ов:
5. Сравнение по значению
Итого:
📲 Мы в 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 — это не просто константы. Это лёгкий способ инкапсулировать поведение и данные, улучшить читаемость и сделать код устойчивее к ошибкам.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍1
Иногда в программе нужна очередь — контейнер, куда элементы добавляются с одной стороны и извлекаются с другой. В Python для этого можно использовать
Однако
Как видно, операция
Для очередей лучше использовать
📲 Мы в MAX
👉@BookPython
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)
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Некоторый код может выводить интересующие вас данные в stdout, вместо того чтобы предоставлять API, возвращающий строку, пригодную для использования в программе.
Вместо рефакторинга такого кода можно воспользоваться менеджером контекста
Также существует
📲 Мы в MAX
👉@BookPython
Вместо рефакторинга такого кода можно воспользоваться менеджером контекста
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.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Чтобы отсортировать словарь по его значениям, используйте функцию
Однако такая функция уже существует в модуле
Вы также можете сортировать только ключи вместо пар ключ-значение:
И снова, эту лямбду можно заменить уже существующим методом:
📲 Мы в 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']
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1😁1
Популярный способ объявить абстрактный метод в Python — это выбросить исключение
Хотя этот подход довольно распространён и даже поддерживается IDE (например, PyCharm считает такие методы абстрактными), у него есть недостаток: ошибка возникает только при вызове метода, а не при создании экземпляра класса.
Чтобы избежать этой проблемы, используйте модуль
Также важно помнить, что
📲 Мы в MAX
👉@BookPython
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)).👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Как упростить работу с аргументами в командной строке с помощью
Раньше для CLI-приложений на Python я использовал
Вот простой пример:
Теперь можно запускать в терминале:
Что круто:
- Автоматически генерируется
- Пишется почти как обычная функция
- Есть автокомплит в оболочках (bash/zsh)
- Поддержка аннотаций типов и валидации "из коробки"
Если ты всё ещё страдаешь с
📲 Мы в MAX
👉@BookPython
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 — синтаксис и подход очень похожи.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🚀 Подборка полезных 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 Свежие новости Москвы
Системное администрирование, 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 Свежие новости Москвы
MAX
Системный Администратор | Sysadmin Windows & Linux Server. Настройка Сети, ПК и Железа. IT Уроки для Сисадмина: Безопасность, Софт…
Блог практикующего админа. Настройка Windows Server, Active Directory (AD), GPO и терминальных серверов (RDP). Работа с Linux: Ubuntu, CentOS, Debian. Сетевое оборудование: Cisco, MikroTik, VPN, DNS, DHCP. Виртуализация (Hyper-V, VMware, Proxmox) и резервное…
❤2👎1
Создание объекта в Python включает два ключевых этапа. Сначала вызывается метод
Однако, если
Важно: не следует создавать экземпляры того же класса в
Пример бесконечной рекурсии:
Пример двойного вызова
Правильный способ:
📲 Мы в 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
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Встроенные значения
Это означает, что вы обычно работаете с приближениями, а не с точными значениями:
Модуль
Но и этого может быть недостаточно:
Для абсолютно точных вычислений можно использовать модуль
Очевидное ограничение — всё равно приходится использовать приближения для иррациональных чисел, таких как π.
📲 Мы в MAX
👉@BookPython
float в 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
Очевидное ограничение — всё равно приходится использовать приближения для иррациональных чисел, таких как π.
👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
💡 Как избежать повторения кода с помощью
Как упростить код и избежать дублирования с помощью
Допустим, у нас есть функция
Вместо того чтобы каждый раз писать это явно, можно создать частичную функцию:
Это удобно, если вы хотите предварительно зафиксировать часть аргументов, например:
* логгеры с предустановленным уровнем
* коннекторы с общими параметрами
* команды CLI с типовыми флагами
Таким образом, вы уменьшаете дублирование и делаете код читаемее. А ещё это красивый способ внедрить DI без фреймворков — просто передайте
📲 Мы в MAX
👉@BookPython
functools.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.👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🚀 Как логировать без боли в Python
Как настроить логирование в Python один раз — и больше к этому не возвращаться.
Обычно начинающие разработчики либо используют
Что мы получаем:
* Удобный формат времени и уровня лога
* Защиту от дублирования логов (если модуль импортируется несколько раз)
* Готовность к масштабированию (можно легко добавить файл-логгер)
Если вы устали от
Пользуетесь ли вы встроенным
📲 Мы в MAX
👉@BookPython
Как настроить логирование в Python один раз — и больше к этому не возвращаться.
Обычно начинающие разработчики либо используют
print(), либо подключают 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?👉@BookPython
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2