Python inheritance: when and why __init__
I'm new to Python trying to understand the philosophy / logic of inheritance methods. The questions ultimately revolve around why and when to use a method __init__
in a subclass. Example:
It seems that a subclass inheriting from a superclass shouldn't have its own constructor (__init__
). Below the dog inherits the attributes (name, age) and methods (makenoise) of the mammal. You can even add a ( do_a_trick
) method . Everything seems to work as it should.
However, if I wanted to add a new attribute to the subclass, as I am trying to do in the Cats class, I get the "self" error. However, I used "I" in defining the class of the dog. What is the nature of the difference? I seem to be defining Cats as 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"
source to share
if I wanted to add a new attribute to a subclass, when I try to do in the Cats class, I get the error "self" is not defined. But I used "I" in defining the class of the dog.
In your superclass Mammal, you have a function __init__
that takes the argument you chose * to call self
. This argument is in scope when you are in the body of a function __init__
- it is a local variable like any local variable and it is not possible to refer to it after its containing function has completed. The function defined in the Dog class do_a_trick
also takes an argument calledself
and it is also local to this function. What makes these variables special is not their name (you could call them whatever), but the fact that, as first arguments to instance methods in python, they receive a reference to the object they are named as their value. (Read this last sentence a few times, this is the key to understanding this, and you probably won't get it the first time.)
Now, in Cat
, you have a line of code that is not in a function at all. At the moment there is nothing in scope including self
, so 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 this argument happened to be the first argument to an instance method on Cat
, then it would have the value of the instance Cat
on which it was called. Otherwise, he would have had everything that was transmitted to him.
* you chose wisely!
source to share
You can do something like Chunky's answer by initializing all the variables in the constructor method i.e. __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 cannot use self
outside of functions is because it self
is explicitly only used for class instances. If you used it outside, it would lead to ambiguity. If my answer is unclear, please let me know in the comments.
source to share
The method __init__
runs once when the class is instantiated. So if you want to set an attribute on an instance when it was created, then where do you do that. self
is a special keyword that is passed as the first argument to each method and refers to the instance itself. __init__
is no different from other methods in this regard.
"What is the nature of the difference": you define a method Dog.do_a_trick
, and you get it self
as an argument to the method, as usual. But in Cat
you inadvertently (perhaps subconsciously!) Tried to work on the class scope - this is how you should set a class attribute whose value is the same for all cats:
class Cat(object):
sound = "meow"
It differs in that you have both options. Sometimes (not all the time, but occasionally) the class attribute is useful. All cats have the same sound. But most of the time you will be working with instance attributes - different cats have different names; if you need it use __init__
.
source to share
Declarations at the top level of a Python class become class attributes. If you're coming from a C ++ or Java background, it's like declaring a static member variable. You cannot assign instance attributes at this level.
A variable self
usually refers to a specific instance of the class, the one from which the method was called. When a method call is made using 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, this argument is called self
inside the body of the method functions. You can think of it self
as a valid identifier in method bodies. Your assignment is self.furry = True
not being executed in the method, so it is not itself defined there.
You basically have two options to achieve what you want. First, correctly define it 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 get into Python, I highly recommend reading these two parts of the Python documentation:
Python Data Model Special Methods (More Complex)
source to share
Suppose you have a named class that has a named Person
method get_name
that is 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 instantiate Person
like p1
. Now when you call a function get_name()
with this instance it will transform internally
Person.get_name(p1)
So self is the instance itself.
Without, self
you can write the code above:
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 just a convention.
And for inheritance, if you want to have additional attributes in your subclass, you need to initialize your superclass and add your parameter the way you wanted. For example, if you want to subclass from Person
named Boy
with new attribute height
, 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
source to share
As stated in other answers, self
what you see in the other functions are actually parameters. By Python convention, the first parameter to an instance method is always self
.
Class Cats
inherits the function __init__
of its base class
Mammals
. You can override __init__
and you can call or not call the base class.
If you Cats
__init__
want to call the underlying implementation but don't want to worry 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 question for something about star notation for a variable number of arguments: Is it possible to pass a variable number of arguments 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'}
source to share