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?

+3


source to share


2 answers


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)

      

+5


source


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.

0


source







All Articles