OOP
Structure your code with objects and classes
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:
- Organization: OOP lets you organize your code in a more structured and modular way.
- Reusability: It makes code reuse easier through inheritance and composition.
- Maintainability: Code that is well structured into objects is generally easier to maintain and evolve.
- 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:
Personneis the name of the class.__init__is a special method (the constructor) that initializes a new object.selfrefers to the current object instance.nomandageare attributes.salueris 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:
Animalis the base class (or parent class).ChienandChatare derived classes (or child classes) that inherit fromAnimal.- The
parlermethod 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