0

I defined an immutable class as explained here. Now I'd like to define a copy method that transfers all properties of a given instance to a new instance and that allows to provide new values for any of the existing properties.

This is what I have:

from operator import itemgetter

class Foo(tuple):
    __slots__ = []

    def __new__(cls, a, b, c):
        return tuple.__new__(cls, (a, b, c))

    a = property(itemgetter(0))
    b = property(itemgetter(1))
    c = property(itemgetter(2))

    def copy(self, a='', b='', c=''):
        return Foo(
            a=a if a else self.a,
            b=b if b else self.b,
            c=c if c else self.c,
        )

Desired behaviour:

instance_a = Foo('a', 'b', 'c')
instance_b = instance_a.copy(c='d')

assert instance_b.a == 'a'
assert instance_b.b == 'b'
assert instance_b.c == 'd'

Question: Is there a more elegant way to pick between the new and existing properties other than the if else construct (i.e., a=a if a else self.a)?

Jan Trienes
  • 2,501
  • 1
  • 16
  • 28

2 Answers2

2

collections.namedtuple in the standard library does the same thing and has a convenient copy method called _replace too:

from collections import namedtuple

Foo = namedtuple('Foo', ('a', 'b', 'c'))

x = Foo(1, 2, 3)
print(x)
y = x._replace(b='hello')
print(y)

outputs

Foo(a=1, b=2, c=3)
Foo(a=1, b='hello', c=3)
AKX
  • 152,115
  • 15
  • 115
  • 172
1

a or self.a
But if bool(a) is evaluated to False it will have unexpected behaviour. But it is already the case in your question.

Louis Saglio
  • 1,120
  • 10
  • 20