Python crazy mutability

98 Views Asked by At

What this code should print ?

class Parent():
    class Meta(object):
        classattr = "Hello"

class Child(Parent):
    pass


Child.Meta.classattr = "world"


ch = Child()
pr = Parent()

ch.Meta.classattr = "Oppa"

print Parent.Meta.classattr
print Child.Meta.classattr

print pr.Meta.classattr
print ch.Meta.classattr

I've expected following:

Hello
world
Hello
Oppa

But got

Oppa
Oppa
Oppa
Oppa

So... i can modify parent class (not instance!) by modifying child class instance. Is that normal ?

3

There are 3 best solutions below

3
On

Child.Meta actually is Parent.Meta - class Childhas no own Meta class attribute so attribute's lookup rules resolve to Parent.Meta. Also since it's a class attribute, any lookup of an instance of of Parent (including instances of Child) will resolve to very same class object.

0
On

I'll try to explain what happens here:

class Parent():
    class Meta(object):
        classattr = "Hello"

class Child(Parent):
    pass


Child.Meta.classattr = "world"

Here you are assigning "world" to classattr of Meta. It doesn't matter if Meta was originally defined inside Parent; Child and Parent shares the same Meta.

ch = Child()
pr = Parent()

ch.Meta.classattr = "Oppa"

Here you are assigning "world" to classattr of Meta. It doesn't matter if ch is an instance. Meta is always the same object.

print Parent.Meta.classattr
print Child.Meta.classattr

print pr.Meta.classattr
print ch.Meta.classattr

Here you are printing the classattr of the same Meta.

0
On

I think the most straightforward explanation is that the metaclass is just a normal object (of type type) referenced by Parent, and subclassing and instantiation do not copy the metaclass but just create a new reference to it (or strictly speaking actually not even that—it's just how attribute lookup works). So anytime you're storing a new value into classattr, you're actually modifying the same attribute of the same object.