Примечание: я сделал это с Python 3.6.7, но документация там не изменилась для NamedTuple
, поэтому я сомневаюсь, что он там изменится.
Итак, я смотрю на класс NamedTuple из пакета ввода и мне интересно, есть ли способ добавить в него изменяемое значение по умолчанию. Первая попытка - посмотреть, смогу ли я использовать метод класса _make
в своих интересах, но потом я обнаружил, что класс проверен на переопределение __new__
.
.
(Напомним: если вы установите []
в значение по умолчанию, вы получите все объекты, имеющие один и тот же список. Это верно как для старого collections.namedtuple
, так и для нового typing.NamedTuple
. Вот почему collections.defaultdict
класс имеет аргумент default_factory
в конструкторе.)
>>> from typing import NamedTuple, List
>>> class Person(NamedTuple):
... name: str
... children: List['Person']
... def __new__(self, name: str, children: List['Person'] = None):
... return Person._make(name, children if children is not None else [])
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/typing.py", line 2163, in __new__
raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
AttributeError: Cannot overwrite NamedTuple attribute __new__
>>>
Я продолжал ломать голову над тем, как проникнуть в упрямый список __new__
, но тогда это вообще не имело смысла.
>>> class Person(NamedTuple):
... name: str
... children: List['Person'] = None
...
>>>
>>> def new_new(name: str, children: List[Person] = None) -> Person:
... return Person(name, [] if children is None else children)
...
>>> old_new = Person.__new__
>>> def new_new(name: str, children: List[Person] = None) -> Person:
... return old_new(name, [] if children is None else children)
...
>>> Person.__new__ = new_new
>>>
>>> Person('John')
Person(name='John', children=None)
>>> Person.__new__
<function new_new at 0x7f776b2b2e18>
>>> new_new('John')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in new_new
File "<string>", line 14, in __new__
TypeError: tuple.__new__(X): X is not a type object (str)
>>> new_new(Person, 'John')
Person(name='John', children=None)
>>>
Итак, как это должно быть сделано? Я привык делать то же самое для collections.namedtuple
классов, и это в основном любопытство.
Во всей практичности я, вероятно, должен сделать это:
class Person(NamedTuple):
name: str
children: List['Person'] = None
@classmethod
def create(cls, name, children=None):
return cls(name, [] if children is None else children)