The inheritance of attributes using __init__
Question:
I’m Java person who just started learning Python. Take this example:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
self.name=name
self.phone=phone
self.website=website
I’m sure there’s a lot of redundant code (I know in Java, there are a lot of redundancies for the bit of code above).
Which parts are redundant with respect to which attributes are already inherited from the parent class?
Answers:
When writing the __init__
function for a class in python, you should always call the __init__
function of its superclass. We can use this to pass the relevant attributes directly to the superclass, so your code would look like this:
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
Person.__init__(self, name, phone)
self.website=website
As others have pointed out, you could replace the line
Person.__init__(self, name, phone)
with
super(Teenager, self).__init__(name, phone)
and the code will do the same thing. This is because in python instance.method(args)
is just shorthand for Class.method(instance, args)
. If you want use super
you need to make sure that you specify object
as the base class for Person
as I have done in my code.
The python documentation has more information about how to use the super
keyword. The important thing in this case is that it tells python to look for the method __init__
in a superclass of self
that is not Teenager
Attributes in Python are not inherited when they are defined in the constructor and parent class constructor is not called, unless you do all of it manually:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def_init_(self, name, phone, website):
Person.__init__(self, name, phone) # Call parent class constructor.
self.website=website
More on it here: http://docs.python.org/tutorial/classes.html#inheritance
__init__
, unlike a java constructor, is not automatically called for every class in the inheritance hierarchy – you need to do it yourself.
In addition, all of your object hierarchies should be rooted at object
(except in special cases).
Your code should read:
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def_init_(self, name, phone, website):
super(Teenager, self).__init__(name, phone)
self.website=website
super
creates a delegate for its second argument (self
), which will call the next function in the list of functions to call for each name (the “method resolution order”). This is not quite the same as calling the superclass method, as happens in Java.
You could also write super(type(self), self).__init__(name, phone)
, but if you inherit further from this class, type(self)
may not be Teenager
, and you could have an infinite recursion. This is one practical consequence of the fact that you are working with a delegate object with a difference MRO, rather than directly calling a superclass constructor.
Slightly cleaner way I like to do this:
class Teenager(Person):
def __init__(self, *args, **kwargs):
self.website=kwargs.pop('website')
super(Teenager, self).__init__(*args, **kwargs)
It doesn’t make much of a difference in this case, but when you have an __init__
with a ton of arguments, it makes life easier.
All of the examples so far have been for Python 2.x but here’s a solution for Python 3.x that makes use of a shorter version of super() and doesn’t inherit from object.
class Person:
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
super().__init__(name, phone)
self.website = website
With reference to @Tom answer who suggested a cleaner solution when a lot of instance attributes are involved, another approach could be the following, i.e. like @Marcin answer with the addition of *args
and **kwargs
:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, website, *args, **kwargs):
self.website = website
super().__init__(*args, **kwargs)
if __name__ == '__main__':
chris = Teenager('www.chris.com', 'chris', '802-250-5159')
I’m Java person who just started learning Python. Take this example:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
self.name=name
self.phone=phone
self.website=website
I’m sure there’s a lot of redundant code (I know in Java, there are a lot of redundancies for the bit of code above).
Which parts are redundant with respect to which attributes are already inherited from the parent class?
When writing the __init__
function for a class in python, you should always call the __init__
function of its superclass. We can use this to pass the relevant attributes directly to the superclass, so your code would look like this:
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
Person.__init__(self, name, phone)
self.website=website
As others have pointed out, you could replace the line
Person.__init__(self, name, phone)
with
super(Teenager, self).__init__(name, phone)
and the code will do the same thing. This is because in python instance.method(args)
is just shorthand for Class.method(instance, args)
. If you want use super
you need to make sure that you specify object
as the base class for Person
as I have done in my code.
The python documentation has more information about how to use the super
keyword. The important thing in this case is that it tells python to look for the method __init__
in a superclass of self
that is not Teenager
Attributes in Python are not inherited when they are defined in the constructor and parent class constructor is not called, unless you do all of it manually:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def_init_(self, name, phone, website):
Person.__init__(self, name, phone) # Call parent class constructor.
self.website=website
More on it here: http://docs.python.org/tutorial/classes.html#inheritance
__init__
, unlike a java constructor, is not automatically called for every class in the inheritance hierarchy – you need to do it yourself.
In addition, all of your object hierarchies should be rooted at object
(except in special cases).
Your code should read:
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def_init_(self, name, phone, website):
super(Teenager, self).__init__(name, phone)
self.website=website
super
creates a delegate for its second argument (self
), which will call the next function in the list of functions to call for each name (the “method resolution order”). This is not quite the same as calling the superclass method, as happens in Java.
You could also write super(type(self), self).__init__(name, phone)
, but if you inherit further from this class, type(self)
may not be Teenager
, and you could have an infinite recursion. This is one practical consequence of the fact that you are working with a delegate object with a difference MRO, rather than directly calling a superclass constructor.
Slightly cleaner way I like to do this:
class Teenager(Person):
def __init__(self, *args, **kwargs):
self.website=kwargs.pop('website')
super(Teenager, self).__init__(*args, **kwargs)
It doesn’t make much of a difference in this case, but when you have an __init__
with a ton of arguments, it makes life easier.
All of the examples so far have been for Python 2.x but here’s a solution for Python 3.x that makes use of a shorter version of super() and doesn’t inherit from object.
class Person:
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, name, phone, website):
super().__init__(name, phone)
self.website = website
With reference to @Tom answer who suggested a cleaner solution when a lot of instance attributes are involved, another approach could be the following, i.e. like @Marcin answer with the addition of *args
and **kwargs
:
class Person():
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Teenager(Person):
def __init__(self, website, *args, **kwargs):
self.website = website
super().__init__(*args, **kwargs)
if __name__ == '__main__':
chris = Teenager('www.chris.com', 'chris', '802-250-5159')