Python Can';namedtuple子类的t set属性

Python Can';namedtuple子类的t set属性,python,class,python-3.x,namedtuple,Python,Class,Python 3.x,Namedtuple,它看起来像或有些相关的线程,但仍然没有弄清楚:) 我试图创建一个名为tuple的子类,并提供不同的初始值设定项,以便以不同的方式构造对象。例如: >>> from collections import namedtuple >>> class C(namedtuple("C", "x, y")) : ... __slots__ = () ... def __init__(self, obj) : # Initialize a C instanc

它看起来像或有些相关的线程,但仍然没有弄清楚:)

我试图创建一个名为tuple的
子类,并提供不同的初始值设定项,以便以不同的方式构造对象。例如:

>>> 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
它似乎构造了一个对象,但我无法读取它的属性:

>>> 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'
>>> 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)
>c=c(1,2)
>>>c.x,c.y
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:“非类型”对象没有属性“x”

我哪里出了问题?如何创建具有多个构造函数或初始值设定项的子类?

命名元组是不可变的,因此您不能在
\uuuu init\uuuu
初始值设定项中操作它们。您唯一的选择是覆盖
\uuuu new\uuu
方法:

class C(namedtuple('C', 'x, y')):
    __slots__ = ()
    def __new__(cls, obj):
        return super(C, cls).__new__(cls, obj.x, obj.y)
请注意,由于
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。如果在
\uuuu new\uuuu
方法中未使用
return
,则默认返回值为
None
,这会导致错误

使用具有
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'
>>> 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.datetime
实例,也是如此;除了他们用不同的方式解释数字

如果要支持变量参数,请执行以下操作:

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)

有两件事:第一,据我所知,你在这里并没有从namedtuple中得到什么。所以也许你应该换一个普通的班。此外,您不能使服务器过载

第二,可能有助于解决问题的其他可能性:

-不要将不同的参数放在构造函数中,而是让一个类在对象外部接受不同类型的参数并使用适当的参数调用构造函数。 -一个可变的namedtuple,它允许默认值,但也允许您按照最初希望的方式编写子类。
-不完全是命名元组,但允许创建任意对象

更改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")
我建议你用这个方法


为什么有双重
\uuuuu init\uuuuu
\uuuu new\uuuuu
方法?只有第二个才算,它会覆盖第一个。Python不会“重载”方法签名。不会重载。。。这意味着我最初的目标是以不同的方式(取决于重载构造函数)创建C实例,实际上是不可行的?这是完全可行的,只是使用不同的范例而已。请参阅下面我的评论,在您的答案下。你说使用工厂方法是可行的,但不使用多个“构造函数”?你可以只使用一个构造函数,但是构造函数可以根据你传入的参数改变行为。谢谢Martijn。在不重载的情况下,我决定使用构造函数(接收
x
y
)和第二个工厂方法(接收
obj
)。不漂亮,也许我更喜欢C++风格的构造函数重载,但我想这是我能用python做的。YonatanSimson:那使用它呢?你能详细说明一下吗?请注意,
instance.\u replace()
返回一个新实例,原始实例保持不变<代码>。\u replace()
不会提高工厂函数的效率。@MartijnPieters True。您可以这样做:
c=c.\u替换(x=30)
。我觉得即使有一些开销,也最好使用现有的功能。编写自己的方法可能会有相同的开销,而没有相同的测试和测试级别confidence@YonatanSimson:你在这里怎么用?用户要求使用工厂方法来创建新对象
\u replace()
在现有实例上效果很好,但这不是本文要求的。这将创建命名元组的新实例,并且对命名元组的所有现有引用仍将访问原始实例,而不是新实例。因此,他们看不到更改。新实例不能分配给旧变量吗?如果在运行时跟踪原始元组中该点的所有变量,则可以。