3

I'm just starting out with Python, but I cannot figure out why I'm having a problem with such simple class inheritance, and despite the common use of the tutorial I've been following, I haven't seen anyone else on Stack Overflow encountering this issue. Here's the code (don't worry, nothing too complicated):

import random
import sys
import os

class Animal:
    __name = ""
    __height = 0
    __weight = 0
    __sound = 0

    def __init__(self, name, height, weight, sound):
        self.__name = name
        self.__height = height
        self.__weight = weight
        self.__sound = sound

    def toString(self):
        return "{} is {} cm tall and {} kilograms and says {}".format(self.__name,
                                                                      self.__height,
                                                                      self.__weight,
                                                                      self.__sound)

cat = Animal ('Whiskers', 33, 10, 'meow')
print(cat.toString())

bird = Animal ('Flutie', 33, 10, 'tweet')
print(bird.toString())

class Dog(Animal):

    def __init__(self, name, height, weight, sound):
        super(Dog, self).__init__(name, height, weight, sound)

    def toString(self):
        return "{} is {} cm tall and {} kilograms and says {}".format(self.__name,
                                                                      self.__height,
                                                                      self.__weight,
                                                                      self.__sound)

spot = Dog ('Spot', 53, 27, "Woof")

print(spot.toString())

...And here's the output:

Whiskers is 33 cm tall and 10 kilograms and says meow
Flutie is 33 cm tall and 10 kilograms and says tweet
Traceback (most recent call last):
  File "C:/.../animal_test.py", line 72, in <module>
    print(spot.toString())
  File "C:/.../animal_test.py", line 65, in toString
    return "{} is {} cm tall and {} kilograms and says {}".format(self.__name,
AttributeError: 'Dog' object has no attribute '_Dog__name'
Taku
  • 31,927
  • 11
  • 74
  • 85
  • 4
    Double underscores in attribute names have a special meaning in python: https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-a-single-and-a-double-underscore-before-an-object-name – Grimmy Jun 11 '17 at 00:46
  • 3
    Aside from the name mangling issues, setting `__name = ''` etc. at the class level probably doesn't do what you think, serves no purpose here, and is likely to lead to problems later. – jwodder Jun 11 '17 at 00:48
  • @ArturR.Czechowski No, that's not relevant here. – PM 2Ring Jun 11 '17 at 00:56
  • @PM2Ring right, I've been checking that on python2 and I've got different error. – ArturFH Jun 11 '17 at 00:59
  • @ArturR.Czechowski Hopefully, the OP _isn't_ using Python2! In Python 2, `Animal` should inherit from `object`, otherwise you get the inferior old-style classes. However, in Python 3 you can call `super` without supplying args, which makes it simpler to use than in Python 2, although scary things happen internally to make that magic work. ;) – PM 2Ring Jun 11 '17 at 01:03
  • 2
    Python != Java. Stop writing double-underscore name-mangled attributes *unless that is what you want*, that, you are trying to create class-local references, which it certainly isn't what you want given that you are trying to access the attributes in a subclass. Stop writing attributes at the class level that you are only going to shadow at the instance level, i.e. `__name = ""` This almost certainly isn't doing what you think it is doing. Don't write a `toString` method, use `__str__`. – juanpa.arrivillaga Jun 11 '17 at 01:09
  • 1
    Out of curiosity, what tutorial are you following? – juanpa.arrivillaga Jun 11 '17 at 01:12
  • The tutorial I'm following is this one: https://www.youtube.com/watch?v=N4mEzFDjqtA, with the relevant part starting at 32 mins. I'm still perplexed why no errors flew up for him... – Platypus Egg Jun 11 '17 at 02:12
  • 2
    @PlatypusEgg I'm highly skeptical of that tutorial. Just giving it a quick look, the person has all the classic signs of someone teaching you *Java in Python*. They use parentheses around conditional statements: `if (something is something): `. They use errant semi-colons ending lines. And the example of the class definition is totally non-pythonic because of the things I mentioned above. He also teaches you to write getters and setters, which is not the you do encapsulation in Python. They are probably coming from either Java/C++ . Get a better Python tutorial if you want to learn Python. – juanpa.arrivillaga Jun 11 '17 at 05:34
  • Good to know! I'm going with Code Academy now instead. – Platypus Egg Jun 12 '17 at 22:45

1 Answers1

4

The double underscores represents name mangling.

class Animal:
    def __init__(self, name, height, weight, sound):
        self.__name = name
        self.__height = height
        self.__weight = weight
        self.__sound = sound

Literally translates to this when being interpreted:

class Animal:
    def __init__(self, name, height, weight, sound):
        self._Animal__name = name
        self._Animal__height = height
        self._Animal__weight = weight
        self._Animal__sound = sound

It doesn’t matter where it’s called, or who called the __init__, the prefix _Animal will take place because it’s physically located under the Animal class.

But when you used the attributes here, as it’s physically located under the Dog class, the got name mangled to this:

class Dog(Animal):

    def __init__(self, name, height, weight, sound):
        super(Dog, self).__init__(name, height, weight, sound)

    def toString(self):
        return "{} is {} cm tall and {} kilograms and says {}".format(self._Dog__name,
                                                                      self._Dog__height,
                                                                      self._Dog__weight,
                                                                      self._Dog__sound)

Which the Dog object definitely doesn’t have an attribute named self._Dog__name, instead it has the attribute self._Animal__name.

Taku
  • 31,927
  • 11
  • 74
  • 85
  • Interesting. Removing the double-underscores from all the attributes made the code work, so thank you. I think I understand how the name-mangling caused the problem. I guess my only question now is why it also worked for the highly-credible tutorial I was following: https://www.youtube.com/watch?v=N4mEzFDjqtA. The class introduction stuff starts at 32 mins. He is using Python version 3.4.2... – Platypus Egg Jun 11 '17 at 01:43
  • That tutorial isn’t particularly a good one...he made the mistake but didn’t explain/catch it in the video. In his actual code, he called the getter functions instead of the underscored attributes. Ie. he did this in his actual code: `self.get_name()` instead of `self.__name()` inside the `Dog` class’s `toString` string formatting – Taku Jun 11 '17 at 02:20
  • Here’s his code: http://www.newthinktank.com/2014/11/python-programming/ , scroll down to where the Dog class is defined, note how his toString function differs from the videos’ – Taku Jun 11 '17 at 02:21
  • I intend to keep watching tutorials in order to start to understand general preferences for certain methods, but if there's one you'd highly recommend for a beginner to Python (and non-markup programming in general), I'd love to know it. – Platypus Egg Jun 11 '17 at 02:24
  • I learned python by reading lots of books :D but the official python tutorial is pretty neat https://docs.python.org/3.6/tutorial/ , and code academy has a hands-on tutorial https://www.codecademy.com/learn/python . For video tutorials, sorry I don’t have any preference... – Taku Jun 11 '17 at 02:27
  • Thanks for the info! I'm working on code academy as we speak, which thankfully takes more time actually going over terminology and rationale behind Python's syntax! – Platypus Egg Jun 12 '17 at 22:44