3

For a class which is intended to be used as immutable, such as:

class Immutable:
    def __init__(self, field):
        self._field = field
    def field(self):
        return self._field

is it ok, to return self in __copy__?

    def __copy__(self):
        return self
Ecir Hana
  • 10,864
  • 13
  • 67
  • 117
  • Not a bit in the above code makes this class immutable. – DeepSpace Feb 09 '17 at 13:14
  • @DeepSpace of course, it's Python. That's why I said "intended to be used". – Ecir Hana Feb 09 '17 at 13:18
  • "of course, it's Python." There **are** ways to make a class immutable in Python. – DeepSpace Feb 09 '17 at 13:20
  • @DeepSpace sure. Above example, named tuple subclass, custom `__setattr__`, ... whatever, is it ok to return `self` from `__copy__`? – Ecir Hana Feb 09 '17 at 13:21
  • No, the above example is not immutable at all. As such, it doesn't matter what `__copy__` returns. – DeepSpace Feb 09 '17 at 13:22
  • @PM2Ring I thought `__copy__` is needed for `copy.copy()`... How does `copy.copy(tuple(...))` work? – Ecir Hana Feb 09 '17 at 13:27
  • http://stackoverflow.com/questions/4828080/how-to-make-an-immutable-object-in-python , especially the answer that mentions `__slots__`. – DeepSpace Feb 09 '17 at 13:27
  • @PM2Ring yes, that's exactly my question: `copy(tuple)` returns new object so I was wondering whether I should reuse `self` or do as tuple does, i.e. create new object with the same content? – Ecir Hana Feb 09 '17 at 13:54
  • @PM2Ring Btw., `copy.copy` seems to work even if I don't declare `__copy__` method... – Ecir Hana Feb 09 '17 at 13:56

1 Answers1

4

If your goal is to make it so that users of your class can safely call copy.copy on its instances, then by all means feel free to add a __copy__ method:

def __copy__(self): 
    return self

However, this isn't really necessary. The documentation for your class should make it clear that its instances are immutable, so anyone using your class should know that they can safely re-use an instance if they want to refer to it in multiple places, just like they do with the built-in immutable types like int, str, tuple, and frozenset. Eg,

a, b, c = Immutable(123), Immutable(456), 42
d = {'one': a, 'two': b, 'three': c}

If you don't supply a __copy__ method and someone does call copy.copy on an instance, then they'll get a new object that's a clone of the original instance. With __copy__ defined as above they will of course just get a reference to the original object.

BTW, you should make .field (and any other attributes) into a property, and don't supply a setter.

class Immutable:
    def __init__(self, field):
        self._field = field

    @property
    def field(self):
        return self._field

And now you can call the field method as if it were a simple attribute:

a = Immutable(123) 
print(a.field)

Of course, you can still mutate the ._field attribute in the normal way, but anyone using your class ought to know they aren't supposed to do that. :)


In the question comments we discussed the fact that calling copy.copy on a tuple returns a reference to the original tuple, even though tuple doesn't define __copy__. None of the built-in types define __copy__, but bear in mind that they are all defined in C, so they work a little differently to classes defined in Python. FWIW, calling copy.copy on any of the built-in immutable types returns a reference to the original object, and calling copy.copy on any of the built-in mutable types returns a clone.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182