Inherited class variable modification in Python

Question:

I’d like to have a child class modify a class variable that it inherits from its parent.

I would like to do something along the lines of:

class Parent(object):
    foobar = ["hello"]

class Child(Parent):
    # This does not work
    foobar = foobar.extend(["world"])

and ideally have:

Child.foobar = ["hello", "world"]

I could do:

class Child(Parent):
    def __init__(self):
      type(self).foobar.extend(["world"])

but then every time I instantiate an instance of Child, “world” gets appended to the list, which is undesired. I could modify it further to:

class Child(Parent):
    def __init__(self):
      if type(self).foobar.count("world") < 1:
          type(self).foobar.extend(["world"])

but this is still a hack because I must instantiate an instance of Child before it works.

Is there a better way?

Asked By: DMack

||

Answers:

You should not use mutable values in your class variables. Set such values on the instance instead, using the __init__() instance initializer:

class Parent(object):
    def __init__(self):
        self.foobar = ['Hello']

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
        self.foobar.append('world')

Otherwise what happens in that the foobar list is shared among not only the instances, but with the subclasses as well.

In any case, you’ll have to avoid modifying mutables of parent classes even if you do desire to share state among instances through a mutable class variable; only assignment to a name would create a new variable:

class Parent(object):
    foobar = ['Hello']

class Child(Parent):
    foobar = Parent.foobar + ['world']

where a new foobar variable is created for the Child class. By using assignment, you’ve created a new list instance and the Parent.foobar mutable is unaffected.

Do take care with nested mutables in such cases; use the copy module to create deep copies if necessary.

Answered By: Martijn Pieters

Assuming you want to have a separate list in the subclass, not modify the parent class’s list (which seems pointless since you could just modify it in place, or put the expected values there to begin with):

class Child(Parent):
    foobar = Parent.foobar + ['world']

Note that this works independently of inheritance, which is probably a good thing.

Answered By: user395760

Passing in an argument to __init__(‘world’) makes it more clear:

class Parent():
    def __init__(self):
        self.foobar = ['Hello']

class Child(Parent):
    def __init__(self, h):
        super().__init__()
        self.foobar.append(h)

g = Child('world')
print(f'g.foobar = {g.foobar}')
p = Child('how are you?')
print(f'p.foobar = {p.foobar}')

Output:

g.foobar = ['Hello', 'world']
p.foobar = ['Hello', 'how are you?']
Answered By: Leon Chang
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.