无法为namedtuple的子类设置属性
它看起来像this或this有些相关的线程,但仍然没有想通的事情了:)无法为namedtuple的子类设置属性
我试图创建的namedtuple
一个子类,并提供不同的初始化器,以便我可以用不同的方式构建对象。例如:
>>> from collections import namedtuple
>>> class C(namedtuple("C", "x, y")) :
... __slots__ =()
... def __init__(self, obj) : # Initialize a C instance by copying values from obj
... self.x = obj.a
... self.y = obj.b
... def __init__(self, x, y) : # Initialize a C instance from the parameters
... self.x = x
... self.y = y
然而,这并不工作:
>>> c = C(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __init__
AttributeError: can't set attribute
经过一番打探(例如,见this线程)我试图用构造函数,而不是初始化:
>>> from collections import namedtuple
>>> class C(namedtuple("C", "x, y")) :
... __slots__ =()
... def __new__(cls, obj) :
... self = super(C, cls).__new__(cls, obj.a, obj.b)
... def __new__(cls, x, y) :
... self = super(C, cls).__new__(cls, x, y)
它似乎构造一个对象,但我不能读取其属性:
>>> c = C(1,2)
>>> c.x, c.y
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'x'
我在哪里错了?我如何创建一个具有多个构造函数或初始化函数的子类?
命名元组是不可变的,所以你不能在__init__
初始值设定项中操作它们。你唯一的选择是重写__new__
方法:
class C(namedtuple('C', 'x, y')):
__slots__ =()
def __new__(cls, obj):
return super(C, cls).__new__(cls, obj.x, obj.y)
注意,因为__new__
是新实例的工厂方法,你需要回报新创建的实例。如果您在__new__
方法中未使用return
,则默认返回值为None
,这会给您提供错误。
演示与x
和y
属性的对象:
>>> class C(namedtuple('C', 'x, y')):
... __slots__ =()
... def __new__(cls, obj):
... return super(C, cls).__new__(cls, obj.x, obj.y)
...
>>> O.x, O.y
(10, 20)
>>> C(O)
C(x=10, y=20)
Python不支持方法重载;通常你可以使用可选的关键字参数或额外的类方法作为工厂方法。
例如,datetime
module有几种这样的工厂方法,可以让您创建不符合标准构造函数的对象。 datetime.datetime.fromtimestamp()
从单个数值创建datetime.datetime
实例,datetime.datetime.fromordinal()
也是如此;除了他们以不同的方式解释数字。
如果你想支持变量参数,做:
class C(namedtuple('C', 'x, y')):
__slots__ =()
def __new__(cls, x, y=None):
if y is None:
# assume attributes
x, y = x.x, x.y
return super(C, cls).__new__(cls, x, y)
这里,y
是一个可选参数,默认为None
,如果不调用者提供:
>>> C(3, 5):
C(x=3, y=5)
>>> C(O)
C(x=10, y=20)
另一种选择,使用类方法,将是:
class C(namedtuple('C', 'x, y')):
@classmethod
def from_attributes(cls, obj):
return cls(obj.x, obj.y)
现在有一个重新制定两种工厂方法一个默认值和一个名为:
>>> C(3, 5):
C(x=3, y=5)
>>> C.from_attributes(O)
C(x=10, y=20)
谢谢Martijn。在不重载的情况下,我决定使用一个构造函数(接收'x'和'y')和第二个工厂方法(接收'obj')。不是很漂亮,也许我更喜欢C++风格的构造函数重载,但我认为这与我可以用Python做的一样多。 – Jens
两件事情:一,你没有真正得到多少出namedtuple这里,至于我可以告诉。所以也许你应该转换到一个普通的班级。此外,您不能重载
其次,其他的可能性可能与您的问题有所帮助:
Factory design pattern - 而不是把不同参数的构造函数,有一个类,需要不同类型的参数,并呼吁具有适当参数的构造函数,在对象之外。 recordtype - 一个可变的namedtuple,允许默认值,但也可以让你按你原先想要的方式编写你的子类。 bunch - 不完全是一个命名的元组,但可以让你创建有些随意的对象。
感谢您的联系,这些都是很好的参考! – Jens
有一种解决方法来更改namedtuple的属性。
import collections
def updateTuple(NamedTuple,nameOfNamedTuple):
## Convert namedtuple to an ordered dictionary, which can be updated
NamedTuple_asdict = NamedTuple._asdict()
## Make changes to the required named attributes
NamedTuple_asdict['path']= 'www.google.com'
## reconstruct the namedtuple using the updated ordered dictionary
updated_NamedTuple = collections.namedtuple(nameOfNamedTuple, NamedTuple_asdict.keys())(**NamedTuple_asdict)
return updated_NamedTuple
Tuple = collections.namedtuple("Tuple", "path")
NamedTuple = Tuple(path='www.yahoo.com')
NamedTuple = updateTuple(NamedTuple, "Tuple")
为什么你有双'__init__'和'__new__'方法?只有第二个是重要的,它会覆盖第一个。 Python不会“重载”方法签名。 –
没有重载...所以这意味着我以不同方式创建C实例的初始目标(取决于重载的构造函数)实际上不可行? – Jens
这是完全可行的,只是使用不同的范例。 –