0%
Object-Oriented Programming in Python

OOP

Structure your code with objects and classes

10-15 min

Object-Oriented Programming in Python

Object-Oriented Programming (OOP) is a programming paradigm that organizes code around objects rather than functions and logic. As a multi-paradigm language, Python offers complete and elegant support for OOP. In this tutorial, we will explore the core concepts of OOP in Python and learn how to use them effectively in your programs.

Why use OOP?

Before diving into the details, it is important to understand why OOP is so popular:

  1. Organization: OOP lets you organize your code in a more structured and modular way.
  2. Reusability: It makes code reuse easier through inheritance and composition.
  3. Maintainability: Code that is well structured into objects is generally easier to maintain and evolve.
  4. Encapsulation: OOP lets you hide complexity and protect data through encapsulation.

Classes and objects

The two fundamental concepts in OOP are classes and objects.

Classes

A class is a blueprint or template for creating objects. It defines the attributes (variables) and methods (functions) that the objects will have.

Here is how to define a simple class in Python:

class Personne:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age
    
    def saluer(self):
        return f"Bonjour, je m'appelle {self.nom} et j'ai {self.age} ans."

In this example:

  • Personne is the name of the class.
  • __init__ is a special method (the constructor) that initializes a new object.
  • self refers to the current object instance.
  • nom and age are attributes.
  • saluer is a method.

Objects

An object is an instance of a class. To create an object, you call the class like a function:

personne1 = Personne("Alice", 30)
personne2 = Personne("Bob", 25)

print(personne1.saluer())  # Prints: Bonjour, je m'appelle Alice et j'ai 30 ans.
print(personne2.saluer())  # Prints: Bonjour, je m'appelle Bob et j'ai 25 ans.

The personne1 and personne2 objects are distinct instances of the Personne class, each with its own attribute values.

Special methods

Python provides several special methods (also called magic methods or dunder methods, for “double underscore”) that let you customize the behavior of objects.

class Livre:
    def __init__(self, titre, auteur, pages):
        self.titre = titre
        self.auteur = auteur
        self.pages = pages
        self.page_actuelle = 0
    
    def __str__(self):
        """Defines how the object is represented as a string."""
        return f"{self.titre} par {self.auteur}"
    
    def __len__(self):
        """Defines the behavior when the len() function is applied to the object."""
        return self.pages
    
    def __del__(self):
        """Method called when the object is deleted."""
        print(f"Le livre {self.titre} a été fermé.")

livre = Livre("Python Facile", "J. Dupont", 200)
print(livre)           # Uses __str__
print(len(livre))      # Uses __len__
del livre              # Triggers __del__

Inheritance

Inheritance allows a class to inherit the attributes and methods of another class. It is a fundamental concept for promoting code reuse.

class Animal:
    def __init__(self, nom):
        self.nom = nom
    
    def parler(self):
        pass  # Method to override in derived classes

class Chien(Animal):
    def parler(self):
        return f"{self.nom} dit Woof!"

class Chat(Animal):
    def parler(self):
        return f"{self.nom} dit Meow!"

animaux = [Chien("Rex"), Chat("Misty")]
for animal in animaux:
    print(animal.parler())

In this example:

  • Animal is the base class (or parent class).
  • Chien and Chat are derived classes (or child classes) that inherit from Animal.
  • The parler method is overridden (redefined) in each derived class.

Multiple inheritance

Unlike some programming languages, Python supports multiple inheritance, which allows a class to inherit from several parent classes.

class A:
    def methode_a(self):
        return "Méthode de A"

class B:
    def methode_b(self):
        return "Méthode de B"

class C(A, B):  # Inherits from A and B
    def methode_c(self):
        return "Méthode de C"

objet = C()
print(objet.methode_a())  # Inherited from A
print(objet.methode_b())  # Inherited from B
print(objet.methode_c())  # Defined in C

Encapsulation

Encapsulation is the concept of bundling data together with the methods that operate on that data, and restricting direct access to an object’s variables.

In Python, encapsulation is achieved by convention rather than strict enforcement:

class CompteBancaire:
    def __init__(self, solde_initial):
        self._solde = solde_initial  # Convention: protected attribute (single underscore)
        self.__compte_id = "123456"  # Convention: private attribute (double underscore)
    
    def deposer(self, montant):
        self._solde += montant
    
    def retirer(self, montant):
        if montant <= self._solde:
            self._solde -= montant
            return True
        return False
    
    def get_solde(self):
        return self._solde

compte = CompteBancaire(1000)
compte.deposer(500)
print(compte.get_solde())  # 1500
print(compte._solde)       # Accessible, but by convention, should not be accessed directly
# print(compte.__compte_id)  # Error: renamed through name mangling
print(compte._CompteBancaire__compte_id)  # Accessible via mangling

In Python:

  • An attribute with a single underscore (_solde) indicates that it is protected (a convention, not enforced).
  • An attribute with a double underscore (__compte_id) is subject to name mangling (renaming), which makes it harder to access accidentally, but not truly private.

Polymorphism

Polymorphism allows objects of different classes to be treated as objects of a common class. In Python, polymorphism is built into the language thanks to duck typing.

def faire_parler(animal):
    print(animal.parler())

chien = Chien("Rex")
chat = Chat("Misty")

faire_parler(chien)  # Rex dit Woof!
faire_parler(chat)   # Misty dit Meow!

The faire_parler function works with any object that has a parler method, regardless of its concrete class.

Properties

Properties let you control access to a class’s attributes while maintaining a clean interface.

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("La température ne peut pas être inférieure au zéro absolu.")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self.celsius = (value - 32) * 5/9

temp = Temperature()
temp.celsius = 25
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")

temp.fahrenheit = 77
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")

Class methods and attributes

Unlike instance methods and attributes, which are specific to each object, class methods and attributes are shared by all instances of the class.

class Voiture:
    # Class attribute
    nombre_de_voitures = 0
    
    def __init__(self, marque, modele):
        self.marque = marque
        self.modele = modele
        # Increment the class attribute
        Voiture.nombre_de_voitures += 1
    
    @classmethod
    def get_nombre_de_voitures(cls):
        return cls.nombre_de_voitures
    
    @staticmethod
    def est_electrique(marque, modele):
        # A static method that does not depend on instance state
        # Fictitious list of electric cars
        voitures_electriques = [("Tesla", "Model 3"), ("Nissan", "Leaf")]
        return (marque, modele) in voitures_electriques

voiture1 = Voiture("Toyota", "Corolla")
voiture2 = Voiture("Honda", "Civic")

print(Voiture.get_nombre_de_voitures())  # 2
print(Voiture.est_electrique("Tesla", "Model 3"))  # True

Conclusion

Object-oriented programming in Python offers a powerful and flexible way to organize your code. It lets you build programs that are more modular, reusable, and maintainable.

In this tutorial, we covered the core concepts of OOP in Python, including classes, objects, inheritance, polymorphism, and encapsulation. But this is only the beginning! There are many other advanced concepts and design patterns that can help you get the most out of OOP.

In the next tutorial, we will explore exception handling in Python, a crucial aspect of writing robust programs.

Comments

Comments are powered by GitHub Discussions

Sign in with GitHub to join the discussion

Link copied!