Python 无法为 namedtuple 的子类设置属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19257497/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can't set attribute for subclasses of namedtuple
提问by Jens
It looks like thisor thisare somewhat related threads, but still haven't figured things out :)
I'm trying to create a subclass of namedtuple
and provide different initializers so that I can construct objects in different ways. For example:
我正在尝试创建一个子类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
However, that doesn't work:
但是,这不起作用:
>>> 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
After some poking around (for example, see thisthread) I tried to use constructors instead of initializers:
经过一番摸索(例如,请参阅此线程),我尝试使用构造函数而不是初始值设定项:
>>> 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)
which seemed to construct an object but then I can't read its attributes:
这似乎构造了一个对象,但后来我无法读取它的属性:
>>> 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'
Where am I going wrong here? How can I create a subclass with multiple constructors or initializers?
我哪里出错了?如何创建具有多个构造函数或初始值设定项的子类?
采纳答案by Martijn Pieters
Named tuples are immutable, so you cannot manipulate them in the __init__
initializer. Your only option is to override the __new__
method:
命名元组是不可变的,因此您不能在__init__
初始化程序中操作它们。您唯一的选择是覆盖该__new__
方法:
class C(namedtuple('C', 'x, y')):
__slots__ = ()
def __new__(cls, obj):
return super(C, cls).__new__(cls, obj.x, obj.y)
Note that because __new__
is a factory method for new instances, you do need to returnthe newly created instance. If you do not use return
in the __new__
method, the default return value is None
, which gives you your error.
请注意,由于__new__
是新实例的工厂方法,因此您确实需要返回新创建的实例。如果您不在return
该__new__
方法中使用,则默认返回值是None
,这会给您带来错误。
Demo with an object with x
and y
attributes:
带有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 does not support method overloading; generally you either use optional keyword arguments or extra class methods as factory methods.
Python 不支持方法重载;通常,您要么使用可选的关键字参数,要么使用额外的类方法作为工厂方法。
The datetime
module, for example, has several such factory methods to let you create objects that do not fit the standard constructor. datetime.datetime.fromtimestamp()
creates a datetime.datetime
instance from a single numeric value, and so does datetime.datetime.fromordinal()
; except that they interpret the number in different ways.
该datetime
模块,例如,有几个这样的工厂方法,让你创建不符合标准的构造函数对象。从单个数值datetime.datetime.fromtimestamp()
创建一个datetime.datetime
实例,也是如此datetime.datetime.fromordinal()
;除了他们以不同的方式解释数字。
If you wanted to support variable arguments, do:
如果您想支持可变参数,请执行以下操作:
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)
Here, y
is an optional argument, defaulting to None
if not supplied by the caller:
这里,y
是一个可选参数,None
如果调用者没有提供,则默认为:
>>> C(3, 5):
C(x=3, y=5)
>>> C(O)
C(x=10, y=20)
The alternative, using a class method, would be:
使用类方法的替代方法是:
class C(namedtuple('C', 'x, y')):
@classmethod
def from_attributes(cls, obj):
return cls(obj.x, obj.y)
Now there are two factory methods; one default and one named:
现在有两种工厂方法;一个默认值和一个命名为:
>>> C(3, 5):
C(x=3, y=5)
>>> C.from_attributes(O)
C(x=10, y=20)
回答by Corley Brigman
Two things: one, you're not really getting much out of namedtuple here, as far as i can tell. So maybe you should just switch to a normal class. Also, you can't overload the
两件事:第一,据我所知,你并没有真正从这里的 namedtuple 中得到多少。所以也许你应该切换到普通班级。此外,你不能超载
Second, other possibilities which might help with your problem:
其次,其他可能有助于解决您的问题的可能性:
Factory design pattern- instead of putting the different parameters in the constructor, have a class that takes different kinds of parameters and calls the constructor with appropriate arguments, outside the object. recordtype- a mutable namedtuple, that allows defaults but would also let you write your subclass the way you originally wanted. bunch- not exactly a named tuple, but lets you create somewhat arbitrary objects.
工厂设计模式- 不是将不同的参数放在构造函数中,而是拥有一个类,该类采用不同类型的参数并在对象外部使用适当的参数调用构造函数。 recordtype- 一个可变的命名元组,它允许默认值,但也可以让您按照最初想要的方式编写子类。 一堆- 不完全是一个命名元组,但可以让你创建一些任意的对象。
回答by chahuja
There is a workaround to changing the attribute of a 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")