11

I have the following code.

class Foo(object):
     def __init__(self):
         self.__baz = 40
     def foo(self):
         print self.__baz

class Bar(Foo):
     def __init__(self):
         #super(Bar, self).__init__()
         self.__baz = 21
     def bar(self):
         print self.__baz

x = Bar()
x.foo()
x.bar()

I get this error:

Traceback (most recent call last):
  File "classes.py", line 15, in <module>
    x.foo()
  File "classes.py", line 5, in foo
    print self.__baz
AttributeError: 'Bar' object has no attribute '_Foo__baz'

why is the foo method not inherited in Bar.

EDIT: It works fine, if you call super which is commented out.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
brain storm
  • 30,124
  • 69
  • 225
  • 393
  • 3
    Not sure, but isn't it because __variablename is a special variable? http://stackoverflow.com/a/1301369/2537322 – meyer9 Sep 27 '13 at 04:07
  • More curiously, why does it *work* if you call `super()`? –  Sep 27 '13 at 04:08
  • Not an answer, but adds more detail - if you change it from `__baz` to `baz` both calls print 21. If you call `super()` and leave as `__baz` they print `40` and `21` respectively. –  Sep 27 '13 at 04:09
  • see also http://stackoverflow.com/q/1301346/748858 – mgilson Sep 27 '13 at 04:21

4 Answers4

10

The double underscore attributes have their names mangled based on the current/containing namespace. In the function foo, the current namespace is Foo so when python looks up self.__baz, it will actually look for self._Foo__baz due to the name mangling scheme. Since nowhere in Foo have you actually set an __baz attribute, the class has no _Foo__baz attribute (it has a _Bar__baz attribute since you set self.__baz in a method within Bar).

Of course, as you've probably noticed, if you call Foo.__init__(self) in Baz.__init__ (directly or via super), you'll see the problem go away because Foo.__init__ sets __baz (i.e. _Foo__baz).

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • so the foo method is not inherited correct? I am a little confused as to what is happening here... – brain storm Sep 27 '13 at 04:21
  • @user1988876 -- the `foo` method *is* inherited. But when you call it, the attribute that it is trying to access is *missing*. When python sees `self.__baz` inside the definition of the `Bar` class, it changes it to `self._Bar__baz`. When it sees `self.__baz` inside the definition of the `Foo` class, it changes it to `self._Foo__baz`. The whole idea here is so that `self.__baz` inside `Foo` can be distinct from `self.__baz` inside `Bar`. – mgilson Sep 27 '13 at 04:23
  • So actually when he does call `super(Bar, self).__init__()` child class Bar's object has **two** attributes one is `_Bar__baz` (because he do `self.__baz = 21` in child class init method) and other is `_Foo__baz` from parent class **?** Whereas in error case (if he doesn't call `super(Bar, self).__init__()`) Bar's object has only one attribute that is `_Bar__baz` **?** – Grijesh Chauhan Sep 27 '13 at 04:35
  • 1
    @GrijeshChauhan -- Exactly. – mgilson Sep 27 '13 at 04:40
  • Thanks, Yesterday only I learned inheritance in Python I don't understand a word "*`current/containing namespace`*" .. what does it mean. – Grijesh Chauhan Sep 27 '13 at 04:43
  • 1
    It means that `Foo.__init__` is in the `Foo` namespace. `Bar.__init__` is in the `Bar` namespace. When python is creating a class, it creates a new namespace (effectively with the name of the class). All of the functions and variables in that class belong to that namespace. So, when you're in `Foo.__init__` the "current namespace" is `Foo`. – mgilson Sep 27 '13 at 04:47
3

When you name variables with double underscore like that in python the member name will be obfuscated. Declaring __baz gives you a member _Bar__baz.

class Bar(Foo):
 def __init__(self):
     #super(Bar, self).__init__()
     self.__baz = 21
 def bar(self):
     print self._Bar__baz

x = Bar()
x.bar()
>>> 21
mgilson
  • 300,191
  • 65
  • 633
  • 696
Aesthete
  • 18,622
  • 6
  • 36
  • 45
  • you have changed the definition of function `bar`. I would like to know why it works when the super call is made, as I made in my edit – brain storm Sep 27 '13 at 04:15
  • + as you added a trick that is helpful for me, but is it not bad practice to use `_Bar__baz` explicitly **?** – Grijesh Chauhan Sep 27 '13 at 04:39
2

By using the initial double underscores on __baz you requested "name mangling" to make a "private" variable. It's documented here:

http://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

If you change the name from __baz to just baz your code will work as shown.

steveha
  • 74,789
  • 21
  • 92
  • 117
1

Since python 3.6, we can now use the __init_subclass__ function, which is called automatically before __init__ of the Child.

class Foo(object):
     def __init_subclass__(self):
         self.__baz = 40
     def foo(self):
         print(self.__baz)

class Bar(Foo):
     def __init__(self):
         self.__baz = 21
     def bar(self):
         print(self.__baz)

x = Bar()
x.foo()
x.bar()

output

40
21
kruserr
  • 463
  • 4
  • 8