1

Let's say we have a class A, a class B that inherits from A and classes C, D and E that inherit from B.

We want all of those classes to have an attribute _f initialized with a default value, and we want that attribute to be mutable and to have a separate value for each instance of the class, i.e. it should not be a static, constant value of A used by all subclasses.

One way to do this is to define _f in A's __init__ method, and then rely on this method in the subclasses:

class A:
    def __init__(self):
        self._f = 'default_value'

class B(A):
    def __init__(self):
        super(B, self).__init__()

class C(B):
    def __init__(self):
        super(C, self).__init__()

Is there any nice Pythonic way to avoid this, and possibly avoid using metaclasses?

Aleksandar Jovanovic
  • 1,127
  • 1
  • 11
  • 14
  • 2
    Note that if all the subclass implementations do is call the superclass implementation, you can leave them out. `class B(A): pass` would work just fine. And are you looking for a *class* attribute or a regular *instance* attribute? Could you give a less abstract example? – jonrsharpe Jun 19 '16 at 14:04
  • Depends if the field `_f` is intended to refer to a mutable or immutable type. – Aya Jun 19 '16 at 14:05
  • Is `_f` supposed to be a class field or an instance field? – user2390182 Jun 19 '16 at 14:06
  • It should be an instance field, and it should be mutable – Aleksandar Jovanovic Jun 19 '16 at 14:12
  • @jonrsharpe I'm really surprised by that, as I have read this post: [link](http://stackoverflow.com/questions/6535832/python-inherit-the-superclass-init) and thought I shouldn't even try it out (a lesson for the future). Thanks. – Aleksandar Jovanovic Jun 19 '16 at 14:17

1 Answers1

1

If your goal is to simplify subclass constructors by eliminating the need the call the base class constructor, but still be able to override the default value in subclasses, there's a common paradigm of exploiting the fact that Python will return the class's value for an attribute if it doesn't exist on the instance.

Using a slightly more concrete example, instead of doing...

class Human(object):

    def __init__(self):
        self._fingers = 10

    def __repr__(self):
        return 'I am a %s with %d fingers' % (self.__class__.__name__, self._fingers)


class MutatedHuman(Human):

    def __init__(self, fingers):
        super(MutatedHuman, self).__init__()
        self._fingers = fingers


print MutatedHuman(fingers=11)
print Human()

...you can use...

class Human(object):

    _fingers = 10

    def __repr__(self):
        return 'I am a %s with %d fingers' % (self.__class__.__name__, self._fingers)


class MutatedHuman(Human):

    def __init__(self, fingers):
        self._fingers = fingers


print MutatedHuman(fingers=11)
print Human()

...both of which output...

I am a MutatedHuman with 11 fingers
I am a Human with 10 fingers

The important point being that the line self._fingers = fingers in the second example doesn't overwrite the default value set on class Human, but merely hides it when referenced as self._fingers.

It's slightly hairy when the variable refers to a mutable type, such as a list. You have to be careful not to perform a operation on the default value which will modify it, although it's still safe to do a self.name = value.

What's neat about this approach is it tends to lead to fewer lines of code than other approaches, which is usually a Good Thing (tm).

Aya
  • 39,884
  • 6
  • 55
  • 55