看起来this或this是一些相关的线程,但仍未解决问题:)
我正在尝试创建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'
我在哪里错了?如何创建具有多个构造函数或初始值设定项的子类?
答案 0 :(得分:27)
命名元组是不可变的,因此您无法在__init__
初始值设定项中对它们进行操作。您唯一的选择是覆盖__new__
方法:
class C(namedtuple('C', 'x, y')):
__slots__ = ()
def __new__(cls, obj):
return super(C, cls).__new__(cls, obj.x, obj.y)
请注意,因为__new__
是新实例的工厂方法,所以您需要返回新创建的实例。如果您未在return
方法中使用__new__
,则默认返回值为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)
答案 1 :(得分:3)
两件事:其中一件事,据我所知,你在这里并没有真正从这个名字中得到很多。所以也许你应该切换到正常的课程。
也不能超载其次,可能有助于解决问题的其他可能性:
Factory design pattern - 不是在构造函数中放置不同的参数,而是使用一个接受不同类型参数的类,并在对象外部使用适当的参数调用构造函数。 recordtype - 一个可变的namedtuple,允许默认值,但也可以让你按照原来想要的方式编写子类。 bunch - 不完全是一个命名元组,但允许你创建一些任意对象。
答案 2 :(得分:3)
我建议您使用_replace
方法
from collections import namedtuple
C = namedtuple('C', 'x, y')
c = C(x=10, y=20)
# c.x = 30 won't work
c = c._replace(x=30)
答案 3 :(得分:1)
有一种解决方法可以更改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")