joesdiner
joesdiner

Reputation: 1125

Python Name Mangling with Private Inheritance

I'm aware of Python name mangling, but am running into unexpected behavahior while using mutliple inheritence. Take for example:

class A(object):
  def __init__(self):
    self.__foo=1
  def bar(self):
    return self.__foo

class B(object):
  pass

class C(B):
  def __init__(self):
    self.test=1

class D(C,A):
  pass

print D().bar()

which gives the error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in bar
AttributeError: 'D' object has no attribute '_A__foo'

However, if I change the definition of "D" to:

class D(A,C):
  pass

it works. Output:

1 

Additionally, if I remove all the member variables from class "C", either definition of D work

class C(B):
  pass

class D1(A, C):
  pass

class D2(C, A):
  pass

print D1().bar()
print D2().bar()

Output:

1
1

Can anyone enlighten me as to what is going on here? I would expect any of these class definitions to behave the same.

Upvotes: 1

Views: 164

Answers (1)

chepner
chepner

Reputation: 530970

In your original definition, class D(C, A), the call to D.__init__ resolved to C.__init__, so A.__init__ was never called and the __foo attribute was never created.

In the modified definition (class D(A, C)), the call to D.__init__ resolved to A.__init__, which did set __foo.

It's important to call parent constructors to ensure that your instances are properly initialized, and unlike Java, parent constructors have to be called explicitly.

For multiple inheritance, there are fewer problems if you use super to ensure every class's __init__ method gets called.

class A(object):
    def __init__(self):
        super(A, self).__init__()
        self.__foo = 1

    def bar(self):
        return self.__foo


class B(object):
    pass


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


class D1(C,A):
    pass


class D2(A,C):
    pass


# Both will print 1
print D1().bar()
print D2().bar()

As long as all classes use super properly, each method will be called once, whether the method resolution order is D1's [C, B, A, object] or D2's [A, C, B, object].

Upvotes: 3

Related Questions