Python classes, why should I return a copy of obj in getters?

I understand that we use getter and setter to prevent users from directly modify class’ data attributes. But why do we have to return a copy or immutable object in getter? Can a user mutate those variables if the returns are mutable?

    def get_encryption_dict(self):
        '''
        Used to safely access a copy self.encryption_dict outside of the class
        
        Returns: a COPY of self.encryption_dict
        '''
        return self.encryption_dict.copy()

enter image description here

enter image description here

Answer

Yes, you need to be careful in python (as in other languages) as some objects are mutable. Most important and common cases are the collections types, like list or dict.

For instance, if the code in your example were this one, then it would have returne the mutable reference to the dictionary!

return self.encryption_dict   # WARNING, do not do that at home / in your own class code, mutable reference returned, oyu probably don't want it!

But why do we have to return a copy or immutable object in getter? Can a user mutate those variables if the returns are mutable?

Yes! For instance, calling .clear() on the returned dict would clear the internal dictionary used by the object… ooops…

You can easily make a copy of the enumerable list or dictionary like in the code example: use .copy() to get a (shallow) copy of all the dictionary, [:] for a list.

return self.encryption_dict.copy()   # much less risk here, the internal dictionary from the object can't be modified, it's a (shallow) copy returned.

As noticed in comments, be careful that if an object is mutable, you getting a proper copy of a list still expose the reference to same object, which can be unexpected. (That’s why we call these “shallow” copies, in opposition with “deep” copies).

As a rule of thumb, thriving for immutability will protect you from subtle (or less subtle) bugs.