#python #inheritance #namedtuple
#питон #наследование #названный корень
Вопрос:
Минимальный воспроизводимый пример:
from collections import namedtuple
class Test(namedtuple('Test', ['a','b'])):
def __init__(self, a, b):
self.c = self.a self.b
def __str__(self):
return self.c
print(Test('FIRST', 'SECOND'))
ВЫХОДНОЙ СИГНАЛ:
FIRSTSECOND
Я думал, что когда __init__
функция определена, она перезаписывает родительскую реализацию. Если это так, то как self.a
они существуют и self.b
существуют с правильными значениями? Если я откажусь a
b
от параметров и __init__
, я получу TypeError: __init__() takes 1 positional argument but 3 were given
. Мне нужно предоставить параметры, но они не задаются явно ни в __init__
том, ни в другом, и у меня нет вызова super()
.
Комментарии:
1. Потому
self.a
что иself.b
не являются атрибутами. Базоваяtuple
структура создается внутриtuple.__new__
. Честно говоря, то, что у вас есть выше, не должно быть именованным кортежем для начала, может быть, просто dataclass, если вы хотите избежать шаблонности? Потому что вышесказанное сводит на нет преимущество экономии местаnamedtuple
полностью2. Мне нужен неизменяемый тип данных с определенным пользователем
__str__
. Я подумалnamedtuple
, что лучшим способом сделать это будет выделение подклассов из3.
namedtuple
это не класс, вы не можете создавать из него подклассы. это фабрика классов .namedtuple('Test', ['a','b'])
возвращает тип,tuple
подкласс. Но то, как вы это сделали здесь, вы сделали свой namedtuple изменяемым для загрузки! (обратите внимание, это должно быть, поскольку вы делаетеself.c = self.a self.b
. Опять же, возможно, рассмотримdataclasses.dataclass
4. @juanpa.arrivillaga Я не думаю, что класс полностью изменяемый. в настоящее время
c
атрибут является изменяемым,a
аb
атрибуты and остаются неизменяемыми. Я хотелc
бы также быть неизменяемым. Я посмотрю наdataclasses.dataclass
спасибо.5. Правильно. «не полностью изменяемый» для меня все еще означает «изменяемый». В любом случае, стремление к строгой неизменности в Python, помимо использования basic
namedtuple
, обычно не стоит усилий и практически всегда может быть легко нарушено, если вы не напишете C-расширение
Ответ №1:
self.a
и self.b
задаются методом именованного кортежа __new__
перед __init__
вызовом. Это связано с тем, что именованный кортеж является неизменяемым (помимо возможности добавлять дополнительные атрибуты, как Test.__init__
и делает), поэтому попытка установить a
и b
после создания кортежа завершится неудачей. Вместо этого значения передаются __new__
таким образом, чтобы значения были доступны при создании кортежа.
Вот пример __new__
переопределения для замены значений a
и. b
class Test(namedtuple('Test', ['a','b'])):
def __new__(cls, a, b, **kwargs):
return super().__new__(cls, b, a, **kwargs)
def __init__(self, a, b):
self.c = self.a self.b
def __str__(self):
return self.c
print(Test('FIRST', 'SECOND')) # outputs SECONDFIRST
Попытка сделать то же самое с __init__
помощью потерпит неудачу:
class Test(namedtuple('Test', ['a','b'])):
def __init__(self, a, b):
self.a, self.b = b, a
self.c = self.a self.b
def __str__(self):
return self.c
print(Test('FIRST', 'SECOND')) # outputs SECONDFIRST
приводит к
Traceback (most recent call last):
File "/Users/chepner/advent-of-code-2020/tmp.py", line 11, in <module>
print(Test('FIRST', 'SECOND'))
File "/Users/chepner/advent-of-code-2020/tmp.py", line 5, in __init__
self.a, self.b = b, a
AttributeError: can't set attribute
Чтобы c
также сделать неизменяемым (сохраняя его отличным от самого кортежа), используйте свойство.
class Test(namedtuple('Test', ['a','b'])):
@property
def c(self):
return self.a self.b
def __str__(self):
return self.c
Обратите внимание, что c
это не отображается или не доступно при обработке экземпляра Test
как обычного кортежа:
>>> x = Test("First", "Second")
>>> x
Test(a='First', b='Second')
>>> len(x)
2
>>> tuple(x)
('First', 'Second')
>>> x[0]
'First'
>>> x[1]
'Second'
>>> x[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
>>> x.c = "foo"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Комментарии:
1. Если бы я хотел
c
также быть неизменяемым, было бы это тем, что я установил__new__
и отказался бы от определения__init__
?2. @ajoseps вам нужно было бы использовать
namedtuple('Test', ['a','b','c'])
тогда, и, возможно,__new__
просто сделать что-то вродеreturn super().__new__(cls, a, b, a b)
3. Нет; тип, возвращаемый
namedtuple
является подклассомtuple
; определенные им атрибуты являются «особенными» не потому, что они инициализируются с помощью__new__
, а из-за того, как тип определен в первую очередь. Если вы хотитеc
, чтобы он был доступен только для чтения, лучше всего было бы сделать его свойством без установщика.4. (Я могу представить случай, когда вы хотите различать значения, которые являются частью кортежа, и значения, которые являются атрибутами кортежа, даже если именованный кортеж разрешает доступ к обоим, используя один и тот же синтаксис.)
5. да, я хочу иметь возможность указать экземпляр типа данных с 2 параметрами и иметь 3-е свойство, производное от 2, но неизменяемое. Я бы хотел избежать явного указания 3 свойств в a
namedtuple
, поскольку это может привести к явному указанию третьего.