testo
testo

Reputation: 1260

dict as class property seems to be same for all instances

I recognize strange behavior in my code. I extract some testcode to demonstrate:

class A:
    def __init__(self):
        super().__init__()
        self.mystr = 'A'
        self.mydict['key'] = 'value'

class B(A):
    mystr = 'B'
    mydict = {}
    def __init__(self, name):
        print(name, 'mystr (before)', self.mystr)
        print(name, 'mydict (before)', self.mydict)
        super().__init__()
        print(name, 'mystr (after)', self.mystr)
        print(name, 'mydict (after)', self.mydict)

if __name__ == '__main__':
    b1 = B('b1')
    b2 = B('b2')

The output is:

b1 mystr (before) B
b1 mydict (before) {}
b1 mystr (after) A
b1 mydict (after) {'key': 'value'}
b2 mystr (before) B
b2 mydict (before) {'key': 'value'}  // <- I expect this to be {}
b2 mystr (after) A
b2 mydict (after) {'key': 'value'}

The property mystr works as I expected. For the second instance b2 it is 'B' as it was initialized. But the property mydict is not initialized correct for the second instance and contains the key/value pair:

b2 mydict (before) {'key': 'value'}

I know that there a mutable and immutable variables in Python and that the first one are hold by reference. But I can explain, why my code is not working as expected.

Can somebody give me a hint?

Upvotes: 0

Views: 42

Answers (1)

chepner
chepner

Reputation: 531055

Yes, you have a single dict shared by all instance of the class. The key is that

self.mydict['key'] = 'value'

is not an assignment to the attribute mydict, so it does not create a new instance attribute. Rather, it's a method call

self.mydict.__setitem__('key', 'value')

and the lookup self.mydict resolves to A.mydict since there is no instance attribute named mydict.

On the other hand,

self.mystr = 'A'

is an assignment to an attribute, so it always creates a new instance attribute if it does not already exist, rather than changing the value of an existing class attribute of the same name. If you want to change the value of a class attribute via an instance of the class, you have to first get a reference to the class, with something like

self.__class__.mystr = 'A'  # Modifies A.mystr without creating self.mystr instead

Upvotes: 1

Related Questions