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

Ric*_*ica 3 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)
Run Code Online (Sandbox Code Playgroud)

现在,如果我换成n1新的Node:

n1 = n1._replace(x=0.5,y=0.5)
print(n1)  # Node(x=0.5,y=0.5)
Run Code Online (Sandbox Code Playgroud)

其他所有项目均未更新:

print(b1)  # Boundary(b=Node(x=0, y=0))
Run Code Online (Sandbox Code Playgroud)

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

我想这样做的是改变这种行为,以便当我改变n1,改变在"感觉" b1,be1,q1,等我怎样才能做到这一点?

Mar*_*ers 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)
Run Code Online (Sandbox Code Playgroud)

像a一样namedtuple,这个类__slots__用来减少内存使用.您可以直接在实例上设置x和y属性,对实例的任何其他引用都将看到更改:

>>> 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)
Run Code Online (Sandbox Code Playgroud)