我正在学习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
时,感受到"感觉"在b1
,be1
,q1
等。我该怎么做?
答案 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__
来减少内存使用。您可以直接在实例上设置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)
答案 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)
这完全解决了问题。