Я пытаюсь преобразовать длинный полый класс данных в именованный кортеж. Мой класс в настоящее время выглядит так:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
После конвертации namedtuple
это выглядит так:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Но здесь есть проблема. Мой оригинальный класс позволил мне передать только значение и позаботился о значении по умолчанию, используя значения по умолчанию для аргументов named / keyword. Что-то вроде:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Но это не работает в случае моего рефакторированного именованного кортежа, так как он ожидает, что я пропущу все поля. Я могу, конечно , заменить вхождения Node(val)
в Node(val, None, None)
но это мне не нравится.
Так есть ли хороший трюк, который может сделать мою переписывание успешным, не добавляя много сложностей кода (метапрограммирование), или я должен просто проглотить таблетку и продолжить поиск и замену? :)
Node
класс просто так, как он есть. Зачем конвертировать в именованный кортеж?Node
и другие классы являются простыми объектами значений держателя данных с кучей различных полей (Node
это только одно из них). Эти объявления классов - не более чем линейный шум, поэтому ИМХО хотел их обрезать. Зачем поддерживать то, что не требуется? :).debug_print()
метода, который прогуливается по дереву и печатает его?BinaryTree
класса.Node
и другие держатели данных не требуют таких специальных методов особ , учитывая , что названные кортежи имеют порядочный__str__
и__repr__
представление. :)Ответы:
Python 3.7
Используйте параметр по умолчанию .
Или еще лучше, используйте новую библиотеку классов данных , которая намного лучше, чем namedtuple.
До Python 3.7
Установите значения
Node.__new__.__defaults__
по умолчанию.До Python 2.6
Установите значения
Node.__new__.func_defaults
по умолчанию.порядок
Во всех версиях Python, если вы устанавливаете меньше значений по умолчанию, чем существует в namedtuple, значения по умолчанию применяются к самым правым параметрам. Это позволяет вам сохранить некоторые аргументы в качестве обязательных аргументов.
Оболочка для Python 2.6 до 3.6
Вот вам обертка, которая даже позволяет (по желанию) установить значения по умолчанию на что-то другое
None
. Это не поддерживает обязательные аргументы.Пример:
источник
isinstance
... все плюсы, нет минусов ... жаль, что вы немного опоздали вечеринка. Это лучший ответ.(None)
это не кортежNone
. Если вы используете(None,)
вместо этого, он должен работать нормально.Node.__new__.__defaults__= (None,) * len(Node._fields)
Я переклассифицировал namedtuple и переопределил
__new__
метод:Это сохраняет интуитивную иерархию типов, чего не делает создание фабричной функции, замаскированной под класс.
источник
__new__
не вызывается, когда_replace
используется.Оберните это в функцию.
источник
isinstance(Node('val'), Node)
: теперь это вызовет исключение, а не возвращает True. Хотя ответ @ justinfay (ниже) немного более подробный, он правильно сохраняет информацию об иерархии типов, поэтому, вероятно, это лучший подход, если другие будут взаимодействовать с экземплярами Node.def make_node(...):
а не притворения, что это определение класса. Таким образом, пользователи не испытывают желания проверять полиморфизм типов в функции, а используют само определение кортежа.isinstance
неправильно.В
typing.NamedTuple
Python 3.6.1+ вы можете предоставить как значение по умолчанию, так и аннотацию типа для поля NamedTuple. Используйте,typing.Any
если вам нужно только первое:Использование:
Кроме того, в случае, если вам нужны как значения по умолчанию, так и необязательная изменчивость, Python 3.7 будет иметь классы данных (PEP 557), которые в некоторых (многих?) Случаях могут заменить именованные кортежи.
Sidenote: одна особенность текущей спецификации аннотаций (выражения после
:
для параметров и переменных и после->
для функций) в Python заключается в том, что они оцениваются во время определения * . Таким образом, поскольку «имена классов становятся определенными после выполнения всего тела класса», аннотации для'Node'
в полях класса выше должны быть строками, чтобы избежать NameError.Этот тип подсказок типа называется "прямой ссылкой" ( [1] , [2] ), и в PEP 563 Python 3.7+ будет иметь
__future__
импорт (который будет включен по умолчанию в 4.0), который позволит использовать прямые ссылки без кавычек, откладывая их оценку.* AFAICT только локальные переменные аннотации не оцениваются во время выполнения. (источник: ПКП 526 )
источник
left
иright
(т.е.Node
) имеет тот же тип, что и определяемый класс, и поэтому должен быть записан как строки.my_list: List[T] = None
self.my_list = my_list if my_list is not None else []
? Разве мы не можем использовать параметры по умолчанию, как это?typing.NamedTuple
. Но с классами данных вы можете использоватьField
объекты сdefault_factory
атрибутом. для этого, заменив вашу идиому наmy_list: List[T] = field(default_factory=list)
.Это пример прямо из документов :
Итак, пример ОП будет следующим:
Однако мне больше нравятся некоторые другие ответы, приведенные здесь. Я просто хотел добавить это для полноты.
источник
_
методом (который в основном означает частный) для чего-то вроде,replace
что кажется довольно полезным ..*args
. Может случиться так, что он был добавлен в язык до того, как многие из этих вещей были стандартизированы._
Префикс , чтобы избежать столкновения с именами пользовательских полей кортежа (соответствующий док цитата: «Любой действительный Python идентификатор может быть использован для имя_поля имен , начинающихся с символа подчеркивания , за исключением.»). Что касается разделенной пробелами строки, я думаю, что это просто для сохранения нескольких нажатий клавиш (и вы можете передать последовательность строк, если хотите)._
имеет большой смысл.Я не уверен, есть ли простой способ только с встроенным именованным кортежем. Есть хороший модуль с именем recordtype, который имеет эту функциональность:
источник
recordtype
безусловно, выглядит интересным для будущей работы. +1recordtype
это изменчиво, аnamedtuple
нет. Это может иметь значение, если вы хотите, чтобы объект был хэшируемым (что, я полагаю, нет, поскольку он начинался как класс).Вот более компактная версия, вдохновленная ответом Justinfay:
источник
Node(1, 2)
это не работает с этим рецептом, но работает в ответе @ justinfay. В противном случае это довольно изящно (+1).В python3.7 + есть совершенно новый аргумент по умолчанию = ключевое слово.
Пример использования:
источник
Коротко, просто и не приводит людей к
isinstance
неправильному использованию :источник
Немного расширенный пример для инициализации всех отсутствующих аргументов
None
:источник
Python 3.7: введение
defaults
param в определение именованных кортежей.Пример как показано в документации:
Узнайте больше здесь .
источник
Вы также можете использовать это:
Это в основном дает вам возможность создать любой именованный кортеж со значением по умолчанию и переопределить только те параметры, которые вам нужны, например:
источник
Сочетание подходов @Denis и @Mark:
Это должно поддерживать создание кортежа с позиционными аргументами, а также в смешанных случаях. Тестовые случаи:
но также поддерживает TypeError:
источник
Мне легче читать эту версию:
Это не так эффективно, так как требует создания объекта дважды, но вы можете изменить это, определив дуплекс по умолчанию внутри модуля и просто сделав функцию выполняющей строку замены.
источник
Поскольку вы используете
namedtuple
в качестве класса данных, вы должны знать, что Python 3.7 представит@dataclass
декоратор для этой цели - и, конечно, он имеет значения по умолчанию.Пример из документов :
Гораздо чище, удобочитаемее и полезнее, чем взлом
namedtuple
. Нетрудно предсказать, что использованиеnamedtuple
s упадет с принятием 3.7.источник
Вдохновленный этим ответом на другой вопрос, вот мое предлагаемое решение, основанное на метаклассе и использующее
super
(для правильной обработки будущих подкассировок). Это очень похоже на ответ Justinfay .Затем:
источник
Ответ jterrace на использование recordtype великолепен, но автор библиотеки рекомендует использовать свой проект namedlist , который обеспечивает реализации как mutable (
namedlist
), так и immutable (namedtuple
).источник
Вот короткий простой ответ с хорошим синтаксисом для именованного кортежа с аргументами по умолчанию:
Использование:
уменьшенная:
источник
Используя
NamedTuple
класс из моейAdvanced Enum (aenum)
библиотеки и используяclass
синтаксис, это довольно просто:Единственным потенциальным недостатком является требование для
__doc__
строки для любого атрибута со значением по умолчанию (это необязательно для простых атрибутов). В использовании это выглядит так:Преимущества, которые это имеет над
justinfay's answer
:это простота, а также
metaclass
основаны, а неexec
основаны.источник
Другое решение:
Использование:
источник
Вот менее гибкая, но более краткая версия оболочки Марка Лодато: она принимает поля и значения по умолчанию в качестве словаря.
Пример:
источник
dict
не имеет гарантии заказа.