Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python inheritance: when and why __init__

I'm a Python newbie, trying to understand the philosophy/logic behind the inheritance methods. Questions ultimately regards why and when one has to use the __init__ method in a subclass. Example:

It seems a subclass inheriting from a superclass need not have its own constructor (__init__) method. Below, a dog inherits the attributes (name, age) and methods (makenoise) of a mammal. You can even add a method (do_a_trick) Everything works as it ``should", it seems.

However, if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined. Yet I used "self" in the definition of the dog class. What's the nature of the difference? It seems to define Cats as I wish I need to use __init__(self,name) and super()__init__(name). Why the difference?

class Mammals(object):

  def __init__(self,name):
    self.name = name
    print("I am a new-born "+ self.name)  
    self.age = 0

  def makenoise(self):
    print(self.name + " says Hello")

class Dogs(Mammals):

  def do_a_trick(self):
    print(self.name + " can roll over")

class Cats(Mammals):

 self.furry = "True"  #results in error `self' is not defined


mymammal = Mammals("zebra") #output "I am a new-born zebra"
mymammal.makenoise()  #output "zebra says hello"
print(mymmmal.age)    #output 0

mydog = Dogs("family pet") #output "I am a new-born family pet"
mydog.makenoise()  #output "family pet says hello"
print(mydog.age)  # output 0
mydog.do_a_trick() #output "family pet can roll over"
like image 813
user3486991 Avatar asked Apr 26 '17 04:04

user3486991


People also ask

Why do you need __ init __ in Python?

The __init__ method is the Python equivalent of the C++ constructor in an object-oriented approach. The __init__ function is called every time an object is created from a class. The __init__ method lets the class initialize the object's attributes and serves no other purpose. It is only used within classes.

When should I use inheritance in Python?

Inheritance allows us to define a class that inherits all the methods and properties from another class. Parent class is the class being inherited from, also called base class.

Do child classes need init?

Absolutely not. The typical pattern is that the child might have extra fields that need to be set that the parent does not have, but if you omit the __init__ method completely then it inherits it from the parent which is the correct behavior in your case.

Can you override __ init __?

Answer #5: Yes, you must call __init__ for each parent class. The same goes for functions, if you are overriding a function that exists in both parents.


7 Answers

Explicit is better than implicit.

However, you can do below:

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        Mammals.__init__(self)

or

class Dogs(Mammals):
    def __init__(self):
        #add new attribute
        self.someattribute = 'value'
        super(Mammals, self).__init__()
like image 67
Chankey Pathak Avatar answered Oct 20 '22 00:10

Chankey Pathak


if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined. Yet I used "self" in the definition of the dog class.

In your superclass, Mammal, you have an __init__ function, which takes an argument that you've chosen* to call self. This argument is in scope when you're in the body of the __init__ function - it's a local variable like any local variable, and it's not possible to refer to it after its containing function terminates. The function defined on the Dog class, do_a_trick, also takes an argument called self, and it is also local to that function. What makes these variables special is not their name (you could call them anything you wanted) but the fact that, as the first arguments to instance methods in python, they get a reference to the object on which they're called as their value. (Read that last sentence again a few times, it's the key to understanding this, and you probably won't get it the first time.)

Now, in Cat, you have a line of code which is not in a function at all. Nothing is in scope at this point, including self, which is why this fails. If you were to define a function in Cat that took an argument called self, it would be possible to refer to that argument. If that argument happened to be the first argument to an instance method on Cat, then it would have the value of the instance of Cat on which it had been called. Otherwise, it would have whatever got passed to it.

*you have chosen wisely!

like image 21
Jon Kiparsky Avatar answered Oct 20 '22 00:10

Jon Kiparsky


Declarations at the top level of a Python class become class attributes. If you come from a C++ or Java background, this is similar to declaring a static member variable. You cannot assign instance attributes at that level.

The variable self usually refers to a specific instance of a class, the one from which the method has been called. When a method call is made using the syntax inst.method(), the first argument to the function is the object inst on which the method was called. In your case, and usually by convention, that argument is named self within the function body of methods. You can think of self as only being a valid identifier within method bodies then. Your assignment self.furry = True does not take place in a method, so self isn't defined there.

You have basically two options for achieving what you want. The first is to properly define furry as an attribute of the cat class:

class Cat(Mammals):
    furry = True

    # Rest of Cat implementation ...

or you can set the value of an instance variable furry in the cat constructor:

class Cat(Mammals):
    def __init__(self):
        super(Mammals, self).__init__(self)
        self.furry = True

    # Rest of Cat implementation ...

If you're getting into Python I highly recommend to read these two parts of the Python documentation:

Python classes

Python data model special methods (more advanced)

like image 25
Quinn Mortimer Avatar answered Oct 19 '22 23:10

Quinn Mortimer


As pointed out in the other answers, the self that you see in the other functions is actually a parameter. By Python convention, the first parameter in an instance method is always self.

The class Cats inherits the __init__ function from its base class, Mammals. You can override __init__, and you can call or not call the base class implementation.

In case the Cats __init__ wants to call the base implementation, but doesn't want to care about the parameters, you can use Python variable arguments. This is shown in the following code.

Class declaration:

class Cats(Mammals):

  def __init__(self, *args):
    super().__init__(*args)
    self.furry = "True"

See, for example, this Stack Overflow question for something about the star notation for variable numbers of arguments: Can a variable number of arguments be passed to a function?

Additional test code:

cat = Cats("cat")
print(vars(cat))

Output:

I am a new-born cat
{'name': 'cat', 'age': 0, 'furry': 'True'}
like image 34
sjjhsjjh Avatar answered Oct 19 '22 23:10

sjjhsjjh


You can do something like in Chankey's answer by initiating all the variables in the constructor method ie __init__

However you can also do something like this

class Cats(Mammals):

 furry = "True"

And then

cat = Cats("Tom")
cat.furry # Returns "True"

The reason you can't use self outside the functions is because self is used explicitly only for instances of the class. If you used it outside, it would lead to ambiguity. If my answer isn't clear please let me know in comments.

like image 29
Abhishek Jebaraj Avatar answered Oct 19 '22 23:10

Abhishek Jebaraj


The __init__ method runs once on the creation of an instance of a class. So if you want to set an attribute on an instance when it's created, that's where you do it. self is a special keyword that is passed as the first argument to every method, and it refers to the instance itself. __init__ is no different from other methods in this regard.

"What's the nature of the difference": you define the method Dog.do_a_trick, and you receive self as an argument to the method as usual. But in Cat you've unintentionally (perhaps subconsciously!) attempted to work on the class scope -- this is how you'd set a class attribute whose value is identical for all cats:

class Cat(object):
    sound = "meow"

It's different so you can have both options available. Sometimes (not all the time, but once in a while) a class attribute is a useful thing to have. All cats have the same sound. But much of the time you'll work with instance attributes -- different cats have different names; when you need that, use __init__.

like image 44
ben author Avatar answered Oct 19 '22 22:10

ben author


Suppose you have a class named Person which has a method named get_name defined as :

 class Person():
     def __init__(self, first_name, last_name):
         self.first_name = first_name
         self.last_name = last_name

     def get_name(self):
         return self.first_name + ' ' + self.last_name

And, you create an instance of Person as p1. Now when you call the function get_name() with this instance, it will converts internally

    Person.get_name(p1)

So, self is the instance itself.

Without self you can write above code as :

 class Person():
     first_name = None
     last_name = None


     def get_name(personobject):
         return personobject.first_name + ' ' + personobject.last_name

What I am trying to say is the name self is a convention only.

And for inheritance, if you would like to have extra attributes in your subclass, you need to initiate your super class first and add your parameter as you wanted. For example, if you want to create a subclass from Person named Boy with new attribute height, the you can define it as:

class Boy(Person):
     def __init__(self, first_name, last_name, height):
         super(Person, self).__init__(first_name, last_name)
         self.height = height
like image 30
Sijan Bhandari Avatar answered Oct 19 '22 22:10

Sijan Bhandari