从 namedtuple 继承的 类 如何使用重新定义的 __init__ 维护对父属性的访问?
How do classes inherited from namedtuple maintain access to parent properties with a redefined __init__?
最小可重现示例:
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
如何存在正确的值?如果我放弃 __init__
中的 a
和 b
参数,我会得到 TypeError: __init__() takes 1 positional argument but 3 were given
。我需要提供参数,但它们也没有在 __init__
中明确设置,而且我没有调用 super()
.
self.a
和 self.b
在调用 __init__
之前由命名元组的 __new__
方法设置。这是因为命名元组是不可变的(除了添加附加属性的能力,如 Test.__init__
所做的),因此尝试设置 a
和 b
after 创建元组会失败。相反,这些值会传递给 __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
请注意,将 Test
的实例视为常规元组时,c
不可见或不可访问:
>>> 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
最小可重现示例:
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
如何存在正确的值?如果我放弃 __init__
中的 a
和 b
参数,我会得到 TypeError: __init__() takes 1 positional argument but 3 were given
。我需要提供参数,但它们也没有在 __init__
中明确设置,而且我没有调用 super()
.
self.a
和 self.b
在调用 __init__
之前由命名元组的 __new__
方法设置。这是因为命名元组是不可变的(除了添加附加属性的能力,如 Test.__init__
所做的),因此尝试设置 a
和 b
after 创建元组会失败。相反,这些值会传递给 __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
请注意,将 Test
的实例视为常规元组时,c
不可见或不可访问:
>>> 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