Object Oriented Programming with Python

Object Oriented Programming with Python

OBJECT-ORIENTED PROGRAMMING

We saw, in the previous chapter, the concept of OOP in JavaScript and the modeling of objects in programming. Now is the time to implement the objects as code. Python is an object-oriented language. Indeed, all the data types that we have handled so far are objects. Now is the time to apply our knowledge of object-oriented programming to simplify the structure of our programs which have become very complex. This new paradigm will allow us in the following chapter the use graphical interfaces.

Implement a class

In addition, We are going to group all the methods, including the special methods, with the keyword class followed by the name of the class and the parent class that the class inherits. If the class does not inherit from any parent class, register the object. Here is the implementation of the Person class that we covered in the previous chapter.

This is the code:

class Person(object):
   def __init__(self):
      self.name = "Dubois"
      self.firstname = "Paul"
      self.placeOfResidence = "Le Havre"
   def move(self, newResidence):
      self.placeOfResidence = newPlaceOfResidence

Let's break down the code above: Our class has two functions, the init function and the move function. The init function is the constructor, which allows variables to be set to their initial state.

Each class can equally be identified by a name that you give. This name should be chosen so that it respects the following instructions:

The class name should be short but indicate its content to make the code easier to read. Must not be a reserved name (See here). Must only contain letters (a → z and A → Z), numbers (0 → 9), and the character _ (underscore). Other symbols are prohibited. Python is case-sensitive, i.e. upper and lower case letters are distinguished (for example book, Book, and BOOK are different variables). It is strongly recommended to name the variables in lower case, and start the words with a capital letter for more readability (example: CarteAPlay). In this course, this naming convention will be applied.

We can also observe the appearance of the word self at the beginning of the variables which indicates that we are working on the instance of the class. Here is a small table showing the syntax of variable names in classes.

**Syntax**    *Visibility*    Scope    

**variable**    *Private*    Current function only    

**self.variable**    *Public*    The whole instance of the object    

**self.__variable**    *Private*    The whole instance of the object

It is also possible to quote a method by preceding it with the double underscore symbol (__). This method will only be accessible inside the class.

A private variable can however be read and modified via specific methods called accessors (reading) and mutators (modifying). This allows modifications or checks to be made to the data before it is returned or modified. It is highly recommended to do this rather than offering the variables publicly. The following example implements this attribute protection:

class Person(object):
    def __init__(self):
        self.__name = "James"
        self.__surname = "Paul"
        self.__PlaceofBirth = "Bamenda"
    def __relocate(self, newPlaceofBirth):
        self.__PlaceofBirth = newPlaceofBirth
    def askname(self):
        return("I am " + self.__name + " " + self.__surname)

Also, in the example above the __move function is accessible only from the object, while the askName method is accessible from the entire program.

Use an object

We equally have already used objects. To create an instance of an class, we will enter instanceClasse = Classe(). It is possible to define the default values of the instance variables by passing them as an argument to the constructor method. Here is an example implementing instantiating a class and passing arguments for the constructor:

class Person(object):
    def __init__(self, lastname, firstname, placeOfResidence):
         self.__name = name
         self.__firstname = firstname
         self.__placeOfResidence = placeOfResidence
    def __move(self, newResidence):
         self.__placeOfResidence = newPlaceOfResidence
    def askName(self):
         return("I am " + self.__firstname + " " + self.__lastname)
person1 = Person("Willy", "Clara", "Bamenda")
person2 = Person("Martin", "Julie", "Douala")
print(person1.askName()) # Prints "I'm Clara Willy"
print(person2.askName()) # Prints "I'm Julie Martin"

Special methods

Python allows you to define special methods that make it easier to use objects. We are going to present a method triggered when we try to convert our object into a character string (str() or print()). This method must be named str. It is also possible to do this to retrieve an integer or a real with the int and float methods respectively. Here is an example:

class Person(object):
        def __init__(self, lastname, firstname, placeOfResidence):
            self.__name = name
            self.__firstname = firstname
            self.__placeOfResidence = placeOfResidence
        def __move(self, newResidence):
             self.__placeOfResidence = newPlaceOfResidence
        def __str__(self):
                return("I am " + self.__firstname + " " + self.__lastname + " and I live at " + self.__placeOfResidence)
person1 = Person("Willy", "Clara", "Bamenda")
person2 = Person("Martin", "Julie", "Douala")
print(person1) # "I'm Clara Willy and I live in Bamenda"
print(person2) # "I'm Julie Martin and I live in Douala"

It is also possible to define special methods allowing to compare the objects with the comparison operators that we have seen previously (see here). This is called rich comparison. Here is the list of special methods associated with comparison operators.

**Comparator**    *Syntax*    Associated method
**a equal to b**    *a == b*    __eq__
**a different from b**    *a != b*    __ne__
**a greater than b**    *a > b*    __gt__
**a greater than or equal to b**    *a >= b*    __ge__
**a less than b**    *a < b*    __lt__
**a less than or equal to b**    *a <= b*    __le__

These methods must have as an argument the object with which to compare the current object. Here is an implementation of these rich comparators:

class Person(object):
    def __init__(self, lastname, firstname, age):
         self.__name = name
         self.__firstname = firstname
         self.__age = age
    def getFirstName(self):
            return(self.__firstname)
    def getAge(self):
            return(self.__age)
    def __lt__(self, otherPerson):
            return(self.__age < otherPerson.getAge())
person1 = Person("Willy", "Clara", 24)
person2 = Person("Martin", "Julie", 27)
if person1 < person2: # Use person1.__lt__(person2)
       print(person1.getFirstName() + " is the youngest. ")
else:
       print(person2.getFirstName() + " is the youngest. ")

Inheritance

Inheritance is materialized by the modification of the argument during the definition of the class. We are going to take the UML model of the animals seen previously.

We will implement these classes using the inheritance mechanisms:

class Animals(object):
    def __init__(self, name):
      self.__name = name
      self.__numberLegs = 0
      self.__position = 0
    def advance(self, numberStep):
       self.__position += numberStep
            return("I'm at position " + str(self.__position))
class Dog(Animals):
    def __init__(self, name):
       Animals.__init__(self, name)
       self.__numberLegs = 4
       self.bark()
   def bark(self):
print("Wow!")
class Chicken(Animals):
   def __init__(self, name):
      Animals.__init__(self, name)
      self.__numberLegs = 2
      self.cackle()
   def cackle(self):
print("Cot!")

Lines 11 and 17 are used to trigger the parent class constructor by passing the necessary arguments to it.