使用namedtuple的_replace方法更新包含nt的其他数据结构的字段

时间:2014-10-31 13:42:13

标签: python

我正在学习namedtuple。我想找到一种方法,使用._replace方法更新namedtuple的所有外观,无论它们在哪里。

假设我有一个节点列表,以及这些节点定义的元素列表(双节点波束,四节点四边形)和边界("一个节点"元素)。

我正在玩这个:

from collections import namedtuple as nt
Node = nt('Node', 'x y')
Beam = nt('Beam', 'i j')
Quad = nt('Quad', 'i j k l')
Boundary = nt('Boundary', 'b')
#Define some nodes:
n1 = Node(0,0)
n2 = Node(0,1)
n3 = Node(1,1)
n4 = Node(1,0)
#And some other things using those nodes:
q1 = Quad(n1,n2,n3,n4)
b1 = Boundary(n1)
be1 = Beam(n1,n4)

现在,如果我将n1替换为新的Node

n1 = n1._replace(x=0.5,y=0.5)
print(n1)  # Node(x=0.5,y=0.5)

其他所有项目均未更新:

print(b1)  # Boundary(b=Node(x=0, y=0))

我理解Python命名和对象模型以及为什么背后:b1.b已设置为对象 Node(0,0)名称n1。因此,当更改n1时,其他命名元组仍然包含与以前相同的对象,而n1获取新对象。

我想要做的是改变这种行为,以便在我更改n1时,感受到"感觉"在b1be1q1等。我该怎么做?

2 个答案:

答案 0 :(得分:5)

namedtuple生成的类的所有实例都是不可变的._replace()创建了一个实例,它甚至不会更新您调用此实例的实例。

因为实例是不可变的,所以使用namedtuple无法做到你想要的。您必须在子类中提供此类功能,从而有效地破坏了不变性。或者只提供您自己的Node自定义类,允许直接对属性进行变异:

class Node(object):
    __slots__ = ('x', 'y')

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return '{0.__class__.__name__}({0.x!r}, {0.y!r})'.format(self)

namedtuple类似,此类使用__slots__来减少内存使用。您可以直接在实例上设置xy属性,对该实例的任何其他引用都将看到更改:

>>> class Node(object):
...     __slots__ = ('x', 'y')
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def __repr__(self):
...         return '{0.__class__.__name__}({0.x!r}, {0.y!r})'.format(self)
... 
>>> n1 = Node(10, 20)
>>> n2 = n1
>>> n2
Node(10, 20)
>>> n1.x = 42
>>> n1.y = 81
>>> n2
Node(42, 81)

答案 1 :(得分:0)

4年和1/2年后:如果我今天这样做的话,我很可能会首先接触dataclasses module

from dataclasses import make_dataclass as md

Node = md('Node', 'x y')
Beam = md('Beam', 'i j')
Quad = md('Quad', 'i j k l')
Boundary = md('Boundary', 'b')

...并且由于这些对象是可变的(与namedtuple不同),因此无需用新实例替换节点,我们只需更新节点位置即可:

n1.x = 0.5
n1.y = 0.5
print(n1)  # Node(x=0.5,y=0.5)

这完全解决了问题。