Use the namedtuple _replace method to update fields of other data structures containing nt
I am learning about namedtuple. I would like to find a way using a method ._replace
to update all appearances namedtuple
wherever they are.
Let's say I have a list of nodes and lists of elements (two-node beams, four-node quads) and boundaries ("one node" elements) defined by those nodes.
I am playing around with this:
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)
Now if I replace with a n1
new one Node
:
n1 = n1._replace(x=0.5,y=0.5)
print(n1) # Node(x=0.5,y=0.5)
None of the other items are updated:
print(b1) # Boundary(b=Node(x=0, y=0))
I understand the name and the Python object model and the reason for this: b1.b
an object was given Node(0,0)
, not a name n1
. Thus, when n1
changed, the other named tuples still contain the same object as before, while n1
receiving a new object.
What I would like to do is change this behavior to change when n1
these changes are "felt" in the b1
, be1
, q1
etc. How can i do this?
source to share
All instances of namedtuple
-derived classes are immutable. ._replace()
creates a new instance, it doesn't even update the single instance you are calling.
Since instances are immutable, you can't do what you want with namedtuple
. You will have to provide such functionality in a subclass, effectively breaking immutability. Or just provide your own Node
custom class that allows you to mutate attributes directly:
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)
As well as namedtuple
, this class uses __slots__
to reduce memory usage. You can set attributes x
and y
directly on the copies, and any other references to the instance will see the change:
>>> 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)
source to share
4 and a half years later: If I were doing this today, I would probably first modulate : dataclasses
dataclasses
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')
... and since these objects are mutable (as opposed to namedtuple
), instead of replacing the node with a new instance, we can simply update the position of the node:
n1.x = 0.5
n1.y = 0.5
print(n1) # Node(x=0.5,y=0.5)
This solves the problem completely.
source to share