Over a million developers have joined DZone.

OOP in Python: Part 2

· DevOps Zone

The DevOps zone is brought to you in partnership with Sonatype Nexus. The Nexus suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

In the first part of this article I introduced the 3 pillars of object oriented programming. I covered the Encapsulation, next big topic is Inheritance (this will be covered in 2 articles).

What is Inheritance in OOP?

Inheritance is a concept in object oriented programming which helps programmers to:

  1. Model is a relationships (not true in every programming language, there are cases when only the implementation is shared)
  2. Reuse Code – helps developers to respect the DRY principle and reuse the existing implementation and logic in code
  3. Extend functionality – there are some cases when the source code of the used classes cannot be changed (others are using it or it’s not public or simply it’s not allowed); in that case extending the functionality of a class can be done by applying inheritance.

Python supports single and multiple inheritance. There are major differences how to implement inheritance and how does it work in Python 2.x and 3.x. All the examples which I’ll give are for Python 3.x.

Single Inheritance in Python

In Python inheritance can be done via the class MySubClass(MyBaseClass) syntax. Basically after the new class name in parenthesis I specify the superclass name.
I’ll stick with the traditional example of Animal being the base (or so called superclass and the different species of animals, called the subclasses.

class Animal:
    __name = None
    __age = 0
    __is_hungry = False
    __nr_of_legs = 0

    def __init__(self, name, age, is_hungry, nr_of_legs):
        self.name = name
        self.age = age
        self.is_hungry = is_hungry
        self.nr_of_legs = nr_of_legs

    #
    # METHODS
    #

    def eat(self, food):
        print("{} is eating {}.".format(self.name, food))
    
    #
    # PROPERTIES
    #
    
    @property
    def name(self):
        return self.__name
    
    @name.setter
    def name(self,new_name):
        self.__name = new_name

    @property
    def age(self):
        return self.__age
    
    @age.setter
    def age(self,new_age):
        self.__age = new_age

    @property
    def is_hungry(self):
        return self.__is_hungry
    
    @is_hungry.setter
    def is_hungry(self,new_is_hungry):
        self.__is_hungry = new_is_hungry

    @property
    def nr_of_legs(self):
        return self.__nr_of_legs
    
    @nr_of_legs.setter
    def nr_of_legs(self,new_nr_of_legs):
        self.__nr_of_legs = new_nr_of_legs

In the Animal class I defined 4 private members (__name, __age, __is_hungry,__nr_of_legs). For all 4 members I created properties, using the decorator @property creation presented in the first part of the OOP in Python article. I created a constructor with 4 parameters (not counting the self parameter) which uses the properties to set the values for the private members. Besides the 4 properties I created a method, called eat(self, food), which prints out X is eating Y, where X is the name of the animal and Y is the food passed in as parameter. The Animal class serves as base class for the Snake class.

class Snake(Animal):
    __temperature = 28    
    
    def __init__(self, name, temperature):
        super().__init__(name, 2, True, 0)
        self.temperature = temperature 

    #
    # METHODS
    #

    def eat(self, food):
        if food == "meat":
            super().eat(food)
        else:
            raise ValueError    

    #
    # PROPERTIES
    #

    @property
    def temperature(self):
        return self.__temperature
    
    @temperature.setter
    def temperature(self,new_temperature):
        if new_temperature < 10 or new_temperature > 40:
            raise ValueError
        self.__temperature = new_temperature

The constructor of the Snake class takes 2 arguments, the name of the snake and it’stemperature. For the __temperature private member I created a property, so I am using that in the constructor to store the value passed to the constructor. In the constructor, first I call the base class’ construct using the super() keyword (there are other methods to invoke the parent class’ constructor but in Python 3.x this is the recommended way). When invoking the base class’ constructor I passed in some predefined values, like nr_of_legs zero, since snakes don’t have legs and is_hungry as True, because snakes tend to be “hungrier” then other animals :)

As in other programming languages I can override methods in Python too. I’ve overwritten the eat method and I added extra logic. In case the food which is given to the snake is not meat, then I’m raising a ValueError, otherwise I’m invoking the eat method which is defined in the base class (Animal).

Multiple Inheritance in Python

Python gives us the possibility to derive from multiple base classes, this is called multiple inheritance. This can be useful if there are classes with different functionality, but these functionalities can be combined and used together.

In Python the syntax for inheriting from multiple classes is very easy, all it needs to be done is to enumerate the base classes within parenthesis after the new class’ name, for example: class MySubClass(MyBaseClass1, MyBaseClass2, MyBaseClass3).

class Phone:
    
    def __init__(self):
        print("Phone constructor invoked.")
    
    def call_number(self, phone_number):
        print("Calling number {}".format(phone_number))
class Computer:

    def __init__(self):
        print("Computer constructor invoked.")

    def install_software(self, software):
        print("Computer installing the {} software".format(software))
class SmartPhone(Phone,Computer):

    def __init__(self):
        super().__init__()

I defined 3 classes (Phone, Computer, SmartPhone), 2 are base classes (Phone and Computer) and one derived class SmartPhone. Logically this seams to be correct, a smartphone can make calls and can install software, so the SmartPhone class can call_number(inherited from Phone class) and can install_software (inherited from the Computer class).

#
# will be discussed later why only the constructor of Phone class was invoked
#
>>> my_iphone = SmartPhone()
Phone constructor invoked. 

>>> my_iphone.call_number("123456789")
Calling number 123456789

>>> my_iphone.install_software("python")
Computer installing the python software

If I look at the constructor if the SmartPhone class, its pretty simple, it invokes the base class’s constructor. That’s correct, it invokes a constructor, the question is from which base class? As it can be seen on the code it invokes only the constructor of the Phone class. The question is why? The response will come in the next part!

The DevOps zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

Topics:

Published at DZone with permission of Gergo Bogdan. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}