0

I have recently started learning python and i am having a hard time how inheritance works in python.

I have created two classes, one is named Animal and another one is named Dog. Dog class inherits Animal class. I have some attributes like name, height, sound etc in Animal class which i want to use in Dog class. I am setting the attributes using init method of Animal class.

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

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

    def set_name(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_height(self, height):
        self.__height = height

    def get_height(self):
        return self.__height

    def set_weight(self, weight):
        self.__weight = weight

    def get_weight(self):
        return self.__weight

    def set_sound(self, sound):
        self.__sound = sound

    def get_sound(self):
        return self.__sound

    def get_type(self):
        print("Animal")

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


cat = Animal("whiskers", 50, 20, "meow")
print(cat.tostring())


class Dog(Animal):
    __owner = None

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

    def set_owner(self, owner):
        self.__owner = owner

    def get_owner(self):
        return self.__owner

    def get_type(self):
        print("dog")

    def tostring(self):
        return '{} is {} cm tall and {} kilograms and says {} His owner is {}'.format(self.__name,
                                                                                      self.__height,
                                                                                      self.__weight,
                                                                                      self.__sound,
                                                                                      self.__owner)

    def multiple_sounds(self, how_many=None):
        if how_many is None:
            print(self.get_sound())
        else:
            print(self.get_sound() * how_many)
my_dog = Dog("spot", 50, 40, "Ruff", "Derek")
print(my_dog.tostring())

Problem is when i try to print all the attributes using object of Dog class, an error is displayed saying "

*line 73, in tostring
    return '{} is {} cm tall and {} kilograms and says {} His owner is {}'.format(self.__name,
AttributeError: 'Dog' object has no attribute '_Dog__name'* 

Can anyone help me find the problem in this code ?

Taufiq Rahman
  • 5,600
  • 2
  • 36
  • 44
Yousaf
  • 27,861
  • 6
  • 44
  • 69

3 Answers3

2

Members which start with double underscore are sort of private, so they are not accessible from cild classes. You should use single underscore. Python does not use implicit access specifiers like c++ or java but instead a special name convention is used: names with _ prefix are for protected and names with __ are for private. Also python does not check that you don't violate that convention, it is considered a bad practice to access protected or private methods outside of the class. If you start a name with double underscore it is mangled in a special way. So __name actually becomes _Animal__name when you use it in an Animal class and _Dog__name when you use it in a Dog.

Alexey Guseynov
  • 5,116
  • 1
  • 19
  • 29
1

The problem is the double underscore prefixes in your names. They trigger name mangling.

You should avoid using these. They don't do what you expect.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
1

Using double underscores in Python makes a variable "private". Its similar to using the private keyword in other languages. You need to remove them if you want to access the attributes outside of the parent class.

The Python documentation touches on this subject:

Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class. (emphasis mine)

Christian Dean
  • 22,138
  • 7
  • 54
  • 87