are python3 mutable attributes shared?

Question:

In the code I instantiate two different objects of the same class, how is it possible that object1 alters object2‘s attributes? How can I keep different “self.mutable” variables? Where’s my mistake? 🙂

thanks

class Class:
    mutable = {}
    immutable = 0

    def change(self):
        self.immutable = 1
        self.mutable["key"] = "value"

    def observe(self):
        print(self.immutable, self.mutable)


object1, object2 = Class(), Class()

object1.change()
object2.observe()
# output is: 0 {'key': 'value'}
Asked By: Leo

||

Answers:

You have defined mutable and immutable on the class level,
therefore both will be shared across all instances of Class. The answers in the linked question explain in detail how to avoid the sharing behavior you are observing, so I will just explain what goes on with your code.

In principle, this sharing is unrelated to the attributes being mutable or immutable, but there are some nuances in your code that can make it confusing.

In the case of mutable, the observed behavior is pretty easy to explain.

First of all, the dict mutable is always the same object in memory:

>>> o1, o2 = Class(), Class()                                                                         
>>> o1.mutable is o2.mutable is Class.mutable                                                         
True

when you mutate mutable in any way, this change will be seen everywhere a reference to that dict is held.

>>> Class.mutable                                                                                     
{}
>>> o2.mutable[1] = 2                                                                                 
>>> o1.change()                                                                                       
>>> Class.mutable                                                                                     
{1: 2, 'key': 'value'}

So far all of this is expected. The tricky part with immutable is that you don’t mutate Class.immutable in change, you assign the attribute immutable to the instance (self) change is being called on!

Before calling change, immutable only exists on the class level and is accessed through the class when looked up on the instance o1 or o2. (Note how the instance dicts of o1 and o2 are empty.)

>>> o1, o2 = Class(), Class()                                                                         
>>> o1.immutable, o2.immutable, Class.immutable                                                       
(0, 0, 0)
>>> o1.__dict__, o2.__dict__, 'immutable' in Class.__dict__                                           
({}, {}, True)

When calling change on o2, you only set the attribute immutable = 1 on the instance o2!

>>> o2.change()                                                                                       
>>> o1.immutable, o2.immutable, Class.immutable                                                       
(0, 1, 0)
>>> o1.__dict__, o2.__dict__, 'immutable' in Class.__dict__                                           
({}, {'immutable': 1}, True)
>>> o1.immutable is Class.immutable                                                                   
True
>>> o2.immutable is Class.immutable                                                                   
False

It’s important to understand that mutable and immutable objects set at the class level are shared in exactly the same way. The only difference is that mutable objects have no methods on them that change them. But if you had done self.mutable = {'key': 'value'} in change and then called change on a specific instance, the mutable attribute defined on the instance would take precedence over the attribute defined at the class level when looking it up on the instance via the dot-notation.

Answered By: timgeb
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.