Python subclass inheritance

12,835

Solution 1

The behaviour you are seeing is exactly what you should expect. When you define a class

>>> class Foo(object): pass
...

you can modify that class -- not instances of it, the class itself -- because the class is just another object, stored in the variable Foo. So, for instance, you can get and set attributes of the class:

>>> Foo.a = 1
>>> Foo.a
1

In other words, the class keyword creates a new type of object and binds the specified name to that object.


Now, if you define a class inside another class (which is a weird thing to do, by the way), that is equivalent to defining a local variable inside the class body. And you know what defining local variables inside the body of a class does: it sets them as class attributes. In other words, variables defined locally are stored on the class object and not on individual instances. Thus,

>>> class Foo(object):
...     class Bar(object): pass
...

defines a class Foo with one class attribute, Bar, which happens itself to be a class. There is no subclassing going on here, though -- the classes Foo and Bar are entirely independent. (The behaviour you have achieved could be replicated as follows:

>>> class Foo(object):
...     class Bar(object): pass
...
>>> class Foo(object): pass
...
>>> class Bar(object): pass
...
>>> Foo.Bar = Bar

.)

So you are always modifying the same variable! Of course you will change the values you see; you have changed them yourself!


Your problem seems to be that you are somewhat confused between instance and class attributes, which are not the same thing at all.

A class attribute is a variable which is defined over the whole class. That is, any instance of the class will share the same variable. For instance, most methods are class attributes, since you want to call the same methods on every instance (usually). You can also use class attributes for things like global counters (how many times have you instantiated this class?) and other properties which should be shared amongst instances.

An instance attribute is a variable peculiar to an instance of the class. That is, each instance has a different copy of the variable, with possibly-different contents. This is where you store the data in classes -- if you have a Page class, say, you would like the contents attribute to be stored per-instance, since different Pages will of course need different contents.

In your example, you want Child1.Subcls.a and Child2.Subcls.a to be different variables. Naturally, then, they should depend on the instance!


This may be a bit of a leap of faith, but are you trying to implement Java-style interfaces in Python? In other words, are you trying to specify what properties and methods a class should have, without actually defining those properties?

This used to be considered something of a non-Pythonic thing to do, since the prevailing consensus was that you should allow the classes to do whatever they want and catch the exceptions which arise when they don't define a needed property or method. However, recently people have realised that interfaces are actually sometimes a good thing, and new functionality was added to Python to allow this: abstract base classes.

Solution 2

Try this

class SubclsParent(object):
    def __init__(self):
         self.a = "Hello"

When you define SubclsParent.a directly on the class, you are defining it as static.

Solution 3

When you use Child1.Subcls, python sees there is no Child1.Subcls, and so checks Parent where it finds it and returns it. The same thing happens for Child2.Subcls. As a result, both of those expressions refer to the same class. Child1 and Child2 do not get their own subclasses of it, rather they have access to the original.

*I am looking to avoid having to create instances, as I am using that feature later. *

I don't understand what you mean here.

Solution 4

Your problem is that when you access the attributes you are accessing inherited routines that were created in the parent class, which all refer to the same variable. You can either make those instance variables, or else you can create the attributes in the child classes to get independent attributes.

Share:
12,835
Conor
Author by

Conor

Updated on June 19, 2022

Comments

  • Conor
    Conor about 2 years

    I am trying to build some classes that inherit from a parent class, which contains subclasses that inherit from other parent classes. But when I change attributes in the subclasses in any children, the change affects all child classes. I am looking to avoid having to create instances, as I am using that feature later.

    The code below boils down the problem. The final line shows the unexpected result.

    class SubclsParent(object):
        a = "Hello"
    
    class Parent(object):
        class Subcls(SubclsParent):
            pass
    
    class Child1(Parent):
        pass
    
    class Child2(Parent):
        pass
    
    Child1.Subcls.a # Returns "Hello"
    Child2.Subcls.a # Returns "Hello"
    Child1.Subcls.a = "Goodbye"
    Child1.Subcls.a # Returns "Goodbye"
    Child2.Subcls.a # Returns "Goodbye" / Should still return "Hello"!
    
  • Skurmedel
    Skurmedel over 13 years
    +1 Good concise answer. General information: Python calls a "static field" "class attributes", a method is a class attribute too, it just happens to be callable.
  • Keith
    Keith over 13 years
    This is not a candidate for metaclasses. The OP doesn't even understand classes and class instances yet.
  • nmichaels
    nmichaels over 13 years
    @Keith: My reading of the question suggested that they do understand class instances from the "using that feature later" bit. Maybe I'm way wrong.
  • Conor
    Conor over 13 years
    Problem with that is that it requires that I create an instance of Subcls before I'm able to access 'a'
  • beer_monk
    beer_monk over 13 years
    The problem is that, when the python interpreter walks through your code and creates the classes, the variable a is a reference to one object, a "hello" string. When you make subclasses, they all just inherit the same static reference. There's only one "hello" object in memory. You would have to define a new string object on each subclass or override this. Or, if you want to be evil in ways that you just really, really, shouldn't do, import the copy module, and when you define the subclass, define a = copy.deepcopy(SubclsParent.a)
  • Conor
    Conor over 13 years
    Unfortunately, the child's subclass's metaclass would run before the child is fully defined. What I am trying to do in the larger scheme is use the child's metaclass to define properties (e.g. 'a') of its subclasses before I begin creating instances of those subclasses. (Sorry if my phrasing of the question is sub-par)
  • Conor
    Conor over 13 years
    I was guessing this was the issue. Just wanted to see if anyone knew of another way around the problem. Thanks.
  • beer_monk
    beer_monk over 13 years
    i was oversimplifying the one "hello" object in memory. the thing there's only one of is the Parent.SubCls class-within-a class. katrielalex is right. That's the static mutable thing and when you change .a you're changing an attribute on a shared static class. it's true there's only one "hello" but if not for the class defined within a class, overwriting the reference to "hello" would do what you want and create a new "a" reference on the child.