The inheritance of attributes using __init__
Asked Answered
T

5

61

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?

Trews answered 13/1, 2012 at 16:37 Comment(0)
B
75

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

Blaseio answered 13/1, 2012 at 16:42 Comment(4)
Note that if you are using Python 2.x, you must explicitly list object as a base class of Person in order to use super(). Otherwise, you have to use the Person.__init__ form.Lukash
@Lukash can you provide a reference for that? I can't find one.Blaseio
docs.python.org/library/functions.html#super indicates that super() is only supported on new-style classes, which in Python 2.x are those that inherit from objectLukash
@Lukash Thanks. I did not realize python classes do not automatically inherit from a common base class the way they do in Java. I have fixed my code.Blaseio
S
30

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.

Sension answered 13/1, 2012 at 16:44 Comment(2)
Upvoted for use of *args and **kwargs (... efficient in the case of a numerous arguments).Amino
Using args and kwargs generally leads to inconsistent behaviour. Explicitly taking a keyword from kwargs means Teenager('a', 'b', 'c') will not work correctly. It fails with a KeyError, as website is args[2] in this case.Faithless
S
23

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
Selfdefense answered 27/3, 2018 at 17:2 Comment(0)
H
7

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

Hollis answered 13/1, 2012 at 16:42 Comment(0)
V
3

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')
Volitive answered 1/2, 2022 at 17:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.