Преобразование класса в именованный кортеж Python: решение
Быстрый ответ
Чтобы задать значения по умолчанию для полей вашего именованного кортежа (NamedTuple), воспользуйтесь модулями collections
и dataclasses
, которые доступны начиная с Python версии 3.7+. Пример кода выглядит так:
from typing import NamedTuple
from dataclasses import dataclass, field
@dataclass
class Point(NamedTuple):
x: int = field(default=0) # Определяем координату X по умолчанию как 0
y: int = field(default=0) # Определяем координату Y по умолчанию как 0
# Значения по умолчанию: Point(x=0, y=0)
# Пользовательские значения: Point(x=5, y=10)
Этот подход сочетает в себе удобство назначения значений по умолчанию и простоту определения именованного кортежа.
Применение значений по умолчанию в более ранних версиях Python
Установка значений по умолчанию в более ранних версиях Python
Если у вас установлена версия Python 3.7 или более ранняя, процесс задания значений по умолчанию может вам показаться немного сложным:
from collections import namedtuple
Node = namedtuple('Node', 'value left right')
# Пришло время задать значения по умолчанию!
Node.__new__.__defaults__ = (None, None, None) # На Python 3.6
Если же вы используете версию Python ранее 3.6, то __defaults__
следует заменить на func_defaults
:
Node.__new__.func_defaults = (None,) * len(Node._fields) # Это будто археологическая находка!
Переопределение метода __new__
Если стандартный подход вам не кажется оптимальным, можно переопределить метод __new__
в подклассе NamedTuple
, устанавливая свои значения по умолчанию:
class Node(namedtuple('NodeBase', 'value, left, right')):
def __new__(cls, value, left=None, right=None):
return super(Node, cls).__new__(cls, value, left, right)
Прототипирование: предвкушение будущего
Почувствуйте себя создателем! Сформируйте прототип и задайте значения по умолчанию с помощью метода _replace
:
prototype_node = Node(None, None, None) # Встречайте наш прототип Node!
# Пришла пора создать новый узел (или экземпляр Node)!
new_node = prototype_node._replace(value='root')
Определение типов с использованием аннотаций типов и значений по умолчанию
Совершенствование типизации в Python 3.6.1+
В Python 3.6.1+ значительно улучшена поддержка значений по умолчанию в typing.NamedTuple
, что делает код более читабельным и привлекательным:
from typing import NamedTuple
class Point(NamedTuple):
x: int = 0
y: int = 0
Data classes: новое слово в Python
Классы данных появились в Python 3.7 и быстро стали полезной заменой именованным кортежам для более сложных структур:
from dataclasses import dataclass
@dataclass
class Point:
x: int = 0
y: int = 0
Визуализация
Проиллюстрируем использование значений по умолчанию для необязательных ключевых аргументов в именованных кортежах на примере рецепта:
from collections import namedtuple
Ingredient = namedtuple('Ingredient', 'name quantity', defaults=[None, '1 spoon'])
# Приступим к приготовлению!
sugar = Ingredient('sugar')
flour = Ingredient('flour', '2 cups')
Для наглядности представим рецепт десерта в виде таблицы:
Рецепт приготовления торта 🎂
| Ингредиент | Количество (по умолчанию) |
| ---------- | ---------------------- |
| Сахар | 1 ложка (✅) |
| Мука | 2 стакана (🔄) |
Идея в том, что поля в именованных кортежах могут иметь заранее определенные значения, словно у вас всегда под рукой находятся любимые специи на кухне.
Расширенные методы и способы избежать ошибок
Использование slots: ограничение динамических атрибутов
Чтобы избежать создания динамических словарей для экземпляров, что может быть неэффективно во время работы с памятью, используйте __slots__
в подклассах namedtuple:
class SlotNode(NamedTuple):
__slots__ = () # Политика 'нетрогай'
value: int
left: 'SlotNode' = None
right: 'SlotNode' = None
Будущее уже здесь: прямая типизация
В Python 3.7+ уже нет необходимости заключать типы в кавычки. Это стало возможным благодаря импорту из __future__
:
from __future__ import annotations
class Tree(NamedTuple):
left: Tree | None = None
right: Tree | None = None
Корректировка ситуации с помощью обертывающей функции
Обертывающая функция может работать как с позиционными, так и с ключевыми аргументами, заданными по умолчанию, будто она супергерой, спасающий мир:
def namedtuple_with_defaults(typename, field_names, default_values=()):
T = namedtuple(typename, field_names)
T.__new__.__defaults__ = (None,) * len(T._fields) # Все под контролем
if isinstance(default_values, dict):
prototype = T(**default_values)
else:
prototype = T(*default_values)
T.__new__.__defaults__ = prototype
return T
Полезные материалы
- collections — Container datatypes — Python 3.9.4 documentation — Официальная документация Python по работе с именованными кортежами.
- python – Named tuple and default values for optional keyword arguments – Stack Overflow — Обсуждение возможностей NamedTuple и значений по умолчанию на форуме Stack Overflow.
- dataclasses — Data Classes — Python 3.9.4 documentation — Информация о классах данных в версии Python 3.7+ и о применении значений по умолчанию.
- python – How to check if an object is an instance of a namedtuple? – Stack Overflow — Обсуждение неизменности именованных кортежей на форуме Stack Overflow.
- isbn:1788294874 – Google Books — Упоминание книги с рекомендациями по использованию языка Python, включая информацию об именованных кортежах.