Accéder au contenu principal

Programmation fonctionnelle et programmation orientée objet dans l'analyse des données

Explorez deux des paradigmes de programmation les plus couramment utilisés en science des données : la programmation orientée objet et la programmation fonctionnelle.
Actualisé 14 nov. 2024  · 15 min de lecture

Une bonne structure est la marque d'un code bien écrit. La nature de cette structure est déterminée par le paradigme de programmation utilisé. Un paradigme de programmation est un ensemble cohérent de principes et de techniques définissant l'approche adoptée lors de la conception et du codage de programmes informatiques.

Il existe de nombreux paradigmes de programmation, tels que les paradigmes procédural, impératif, déclaratif, orienté objet et fonctionnel. Certains langages sont limités à quelques paradigmes seulement. Par exemple, SQL est un langage déclaratif. Cependant, de nombreux langages modernes, dont Python, prennent en charge plusieurs paradigmes de programmation, ce qui permet une certaine flexibilité.

Entraînez-vous à écrire des fonctions pures grâce à un exercice pratique tiré de notre cours Introduction aux paradigmes de la programmation. 

De nombreux programmeurs adoptent par défaut un paradigme de programmation préféré, souvent celui qu'ils ont appris en premier. Les programmeurs plus avancés comprennent les avantages et les faiblesses de ces paradigmes de programmation et peuvent tirer parti de chacun de leurs points forts dans différents scénarios. Passons en revue deux des paradigmes de programmation les plus couramment utilisés en science des données : la programmation fonctionnelle et la programmation orientée objet.

image1.png

Qu'est-ce que la programmation fonctionnelle (PF) ?

Dans la programmation fonctionnelle, le code est construit en définissant et en utilisant des fonctions qui dictent la manière dont le programme doit fonctionner. Il s'agit d'un sous-type du paradigme de la programmation déclarative.

Les fonctions sont des lignes de code qui reçoivent des arguments et renvoient des résultats. Une fonction simple peut prendre une liste de nombres comme argument et ne renvoyer que les valeurs paires, par exemple.

def get_even_numbers(list):
    return [num for num in list if num % 2 == 0]

Consultez ces cours pour apprendre les meilleures pratiques d'écriture de fonctions en Python et R.

La programmation fonctionnelle se distingue par sa traçabilité et sa prévisibilité. En effet, les fonctions utilisées sont immuables, c'est-à-dire qu'elles ne peuvent pas être modifiées. Les fonctions sont définies, souvent dans une section distincte du code (ou parfois dans un fichier différent), puis utilisées dans l'ensemble du code selon les besoins. Cet attribut permet de déterminer facilement ce qui se passe dans une section du code, car la fonction fonctionne de la même manière et est appelée de la même manière partout.

Le concept d'immutabilité s'étend aux structures de données dans la programmation fonctionnelle. En PF, il est préférable de créer une copie des données lorsque l'on modifie leur état. Cela permet d'établir une trace écrite virtuelle de toutes les transformations appliquées aux données, ce qui améliore la traçabilité et simplifie l'identification des problèmes. Toutefois, il est important de noter que cette approche entraîne souvent une augmentation de la consommation de mémoire, car chaque changement d'état implique la copie de l'ensemble des données.

Un sous-ensemble de fonctions utilisées dans la programmation fonctionnelle sont des fonctions d'ordre supérieur. Il s'agit de fonctions qui prennent une fonction comme argument ou renvoient une fonction comme résultat. Par exemple, vous pouvez avoir une fonction d'ordre supérieur qui exécute une fonction plusieurs fois, comme celle présentée ci-dessous, ou une fonction qui choisit la bonne fonction à utiliser dans un scénario spécifique.

def run_function_multiple_times(function, num_times):
	for i in range(num_times):
        function()

Avantages de la programmation fonctionnelle dans l'analyse des données

La programmation fonctionnelle fournit une structure permettant de réduire la duplication de votre code. L'objectif de la programmation fonctionnelle est de faciliter l'expression d'actions répétées à l'aide de verbes de haut niveau.

Par exemple, supposons que vous deviez modifier des chaînes de caractères pour les adapter à un format particulier à plusieurs endroits différents de votre code. Ce format exige que vous supprimiez les espaces, la ponctuation et les chiffres et que vous mettiez tout en minuscules. Vous pouvez copier et coller plusieurs lignes de code dans différentes parties de votre programme, selon vos besoins. Mais cette duplication alourdirait votre code. Au lieu de cela, vous pouvez simplement définir une fonction pour modifier vos chaînes de caractères et appeler cette fonction aux endroits appropriés. Cela permet de rationaliser votre code pour le rendre plus fin, plus lisible et plus rapide à taper.

def clean_string(string):
	exclude = set(string.punctuation + string.digits + " ")
    return ''.join(ch.lower() for ch in string if ch not in exclude)
 
my_string = "Hello, World! 123"
new_string = clean_string(my_string)
print(new_string)

Composer des fonctions pour construire des pipelines de données complexes

La programmation fonctionnelle est utile lorsque l'on travaille avec de grands ensembles de données ou des pipelines de données, car elle permet de décomposer des tâches complexes de traitement de données en étapes plus petites et plus faciles à gérer. Souvent, au moins une fonction sera créée pour chaque étape de la filière. Si vous avez rédigé un organigramme des étapes de votre pipeline, vous aurez une idée approximative des fonctions dont vous aurez besoin. D'une manière générale, vous aurez besoin d'au moins une fonction pour chaque étape.

Diagramme du plan du pipeline de données

La plupart des langages disposent également de nombreuses fonctions intégrées et de bibliothèques de fonctions supplémentaires téléchargeables. Le paquet purr de R est un excellent exemple. Il est ainsi plus facile de construire rapidement un pipeline tout en minimisant le nombre de fonctions personnalisées que vous devez créer.

Créer un code expressif et lisible en utilisant les principes FP

La programmation fonctionnelle vise à rendre le code très lisible. Il existe quelques lignes directrices pour construire un code expressif et lisible à l'aide de la programmation fonctionnelle :

  • Veillez à ce que les fonctions soient limitées et ciblées: Chaque fonction doit avoir une responsabilité claire et unique, ce qui en facilite la compréhension et la maintenance. C'est ce qu'on appelle les fonctions pures.
  • Évitez les états mutables (changeants): Évitez de modifier l'état de vos données afin d'éliminer les effets secondaires et de rendre le flux de données plus prévisible. Les structures de données immuables contribuent à la clarté du code et au raisonnement. Au lieu de modifier l'état d'une structure, créez une nouvelle structure de données avec les modifications souhaitées.
  • Tirez parti des fonctions d'ordre supérieur: Les fonctions qui prennent d'autres fonctions comme arguments ou qui renvoient des fonctions sont appelées fonctions d'ordre supérieur. Ils vous permettent d'écrire un code plus expressif et plus concis.
  • Utilisez la composition des fonctions: La composition de fonctions est le processus qui consiste à combiner deux ou plusieurs fonctions pour en produire une nouvelle. Cela vous permet de décomposer des problèmes complexes en éléments plus petits et plus faciles à gérer.

Qu'est-ce que la programmation orientée objet (POO) ?

La programmation orientée objet est un paradigme qui se concentre sur les objets. Les objets sont des entités autonomes composées à la fois de données et de méthodes qui opèrent sur ces données. C'est une façon de maintenir les données et leurs opérations à proximité les unes des autres, contrairement à la programmation fonctionnelle, où elles sont souvent séparées. Cela peut faciliter le suivi de l'évolution de vos données.

Dans la programmation orientée objet, les données et les méthodes sont organisées en objets définis par leur classe. Les classes sont conçues pour dicter le comportement de chaque objet, puis les objets sont conçus à l'intérieur de cette classe. Consultez le tutoriel de DataCamp pour plus de détails sur le fonctionnement des classes en Python.

Exemple OOP

Par exemple, vous pouvez avoir une classe appelée BankAccount qui décrit le comportement de chaque compte. Cette classe peut avoir des attributs tels que le solde, le numéro d'identification et le nom du client. Elle peut également avoir des méthodes qui décrivent comment les informations de cette classe peuvent être affectées, comme le retrait, le dépôt et la vérification du solde.

Une fois cette classe définie, vous pouvez créer un objet, en l'occurrence un compte pour une personne. Une fois cet objet créé, vous pouvez le remplir avec les données de chaque propriété et utiliser les méthodes pour modifier le compte. Voir l'exemple ci-dessous.

# Define a class representing a bank account
class BankAccount:
    def __init__(self, account_id, customer_name, initial_balance=0.0):
        # Initialize account properties
        self.account_id = account_id
        self.customer_name = customer_name
        self.balance = initial_balance

    # Method to deposit money into the account
    def deposit(self, amount):
        """Deposit money into the account."""
        if amount > 0:
            # Update balance and print deposit information
            self.balance += amount
            print(f"Deposited ${amount}. New balance: ${self.balance}")
        else:
            # Print message for invalid deposit amount
            print("Invalid deposit amount. Please deposit a positive amount.")

    # Method to withdraw money from the account
    def withdraw(self, amount):
        """Withdraw money from the account."""
        if 0 < amount <= self.balance:
            # Update balance and print withdrawal information
            self.balance -= amount
            print(f"Withdrew ${amount}. New balance: ${self.balance}")
        elif amount > self.balance:
            # Print message for insufficient funds
            print("Insufficient funds. Withdrawal canceled.")
        else:
            # Print message for invalid withdrawal amount
            print("Invalid withdrawal amount. Please withdraw a positive amount.")

    # Method to check the current balance of the account
    def check_balance(self):
        """Check the current balance of the account."""
        print(f"Current balance for {self.customer_name}'s account (ID: {self.account_id}): ${self.balance}")

Encapsulation

Ce processus consistant à regrouper vos données et votre comportement dans un objet défini par une classe s'appelle l'encapsulation. Il vous permet de créer une structure bien définie qui permet au reste du code d'interagir facilement avec l'objet. Les variables stockées dans l'objet sont appelées attributs et les fonctions qui déterminent le comportement de l'objet sont appelées méthodes.

Inheritance

Une façon de réduire la duplication du code est d'utiliser l'héritage lors de la création de nouvelles classes. Il s'agit d'un moyen de créer de nouvelles classes qui conservent les fonctionnalités des classes parentes. Dans notre exemple de compte bancaire, vous pourriez avoir une classe mère généralisée pour tous les comptes et des classes enfants spécifiques pour les comptes d'épargne et les comptes courants.

Polymorphisme

Le polymorphisme est un concept qui vous permet d'utiliser le même nom pour différentes méthodes qui ont des comportements différents en fonction de l'entrée. Cette méthode est couramment utilisée dans le cadre de l'héritage.

Par exemple, supposons que nous ayons une classe mère appelée Shape qui possède une méthode pour calculer la surface de la forme. Vous pouvez avoir deux classes d'enfants, Circle et Square. Chacune d'entre elles dispose de la méthode appelée Zone, mais la définition de cette méthode est différente pour chaque forme. Comparez les méthodes de calcul de l'aire pour les différentes formes dans le bloc de code ci-dessous.

# Define the parent class Shape
class Shape:
	# Initialize the attributes for the shape
	def __init__(self, name):
    	    self.name = name
	
	# Define a generic method for calculating the area
	def area(self):
    	    print(f"The area of {self.name} is unknown.")
 
# Define the child class Circle that inherits from Shape
class Circle(Shape):
	# Initialize the attributes for the circle
	def __init__(self, name, radius):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.radius = radius
	
	# Override the area method for the circle
	def area(self):
    	# Use the formula pi * r^2
    	    area = 3.14 * self.radius ** 2
    	    print(f"The area of {self.name} is {area}.")
 
# Define the child class Square that inherits from Shape
class Square(Shape):
	# Initialize the attributes for the square
	def __init__(self, name, side):
    	# Call the parent class constructor
    	    super().__init__(name)
    	    self.side = side
	
	# Override the area method for the square
	def area(self):
    	# Use the formula s^2
    	    area = self.side ** 2
    	    print(f"The area of {self.name} is {area}.")

Il existe plusieurs ressources pour apprendre à utiliser la programmation orientée objet, notamment les cours de DataCamp sur la POO en Python, la POO en R et le tutoriel sur la POO en Python.

Avantages de la programmation orientée objet dans l'analyse des données

La programmation orientée objet peut offrir plusieurs avantages à l'analyse des données :

  • Encapsulation des données : L'encapsulation permet de masquer les détails internes des algorithmes et des structures d'analyse des données, en n'exposant que les interfaces nécessaires. Cela favorise une séparation nette des composants et améliore la maintenabilité du code.
  • Réutilisation du code : La programmation orientée objet encourage la création de classes génériques qui peuvent être héritées par d'autres classes, évitant ainsi la duplication du code et réduisant l'effort de maintenance. Cela peut améliorer la productivité et l'efficacité de l'analyse des données.
  • Avantages de la conception : La modélisation des problèmes du monde réel à l'aide de classes et d'objets qui représentent les données et le comportement améliore la lisibilité, la clarté et la modularité de l'analyse des données.

OOP vs. FP : Choisir le bon paradigme

De nombreux facteurs doivent être pris en compte lorsque vous choisissez d'utiliser la POO ou la PF pour votre projet de données. Certaines équipes de données peuvent préférer fortement un paradigme à l'autre, auquel cas il est préférable de faire correspondre votre paradigme à celui de votre équipe. Dans d'autres cas, la nature de vos données ou de vos applications peut faire pencher la balance dans un sens ou dans l'autre.

Quand tirer parti de la POO pour les tâches liées aux données ?

La programmation orientée objet est excellente pour les applications où vous devez modéliser des entités. La conception d'un système de gestion d'une bibliothèque ou la création d'un système permettant d'évaluer chaque entreprise d'un portefeuille d'investissement en sont de bons exemples. Le processus de création d'objets encapsulés facilite le suivi de ces entités. La POO est également préférable lorsque vous disposez d'une mémoire limitée, car la PF a tendance à nécessiter beaucoup plus de mémoire que la POO.

Quand utiliser FP pour les tâches liées aux données

La programmation fonctionnelle excelle dans les tâches d'analyse de données qui nécessitent un traitement parallèle. L'accent mis sur l'immutabilité et l'évitement des effets secondaires facilite la parallélisation des opérations sur un ensemble de données. Les fonctions peuvent être appliquées simultanément sur différentes parties des données sans interférence.

L'accent mis par FP sur l'immutabilité le rend également idéal pour les pipelines de transformation des données. Les fonctions peuvent être composées pour créer une série de transformations sans modifier l'état, ce qui permet d'obtenir un code plus lisible et plus facile à maintenir.

Les scientifiques et les universitaires préfèrent généralement le PC, car il est davantage axé sur les mathématiques pures.

Combinaison des techniques OOP et FP

Dans certaines applications pratiques, la POO et la PF peuvent être combinées dans un programme. Cela permet aux développeurs d'exploiter les points forts de chaque paradigme. L'encapsulation de la POO et l'expressivité de la PF peuvent toutes deux être exploitées pour construire un pipeline d'apprentissage automatique ou une application web.

Cependant, la combinaison de tels paradigmes peut poser des problèmes et doit être effectuée avec prudence. Il peut être plus difficile de déchiffrer un code qui saute entre plusieurs paradigmes. Il peut également être difficile d'assurer une intégration harmonieuse des composants.

Prenons l'exemple suivant. Ce code Python combine OOP et FP, pour effectuer des opérations mathématiques sur une liste de nombres. La classe MathOperation agit comme un conteneur pour une opération mathématique spécifique créée à l'aide de FP. Les fonctions filter_odd_numbers et square_operation permettent de filtrer les nombres impairs et de calculer le carré d'un nombre, respectivement. En utilisant ces composants ensemble, le code filtre efficacement les nombres impairs d'une liste donnée et les élève au carré, démontrant ainsi comment la POO et la PF peuvent fonctionner ensemble pour simplifier et organiser les tâches d'analyse de données.

# Object-Oriented Programming (OOP) - Class Definition
class MathOperation:
	def __init__(self, operation):
    	    self.operation = operation
 
	def apply_operation(self, number):
    	    return self.operation(number)
 
# Functional Programming (FP) - Higher-Order Functions
def filter_odd_numbers(numbers):
    return list(filter(lambda x: x % 2 != 0, numbers))
 
def square_operation(number):
    return number ** 2
 
def process_numbers(numbers, operation_function):
    return list(map(operation_function, numbers))
 
# Example Usage:
# Define an OOP class representing a mathematical operation (square in this case)
square_operation_instance = MathOperation(square_operation)
 
# Sample list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Use FP to filter odd numbers
odd_numbers = filter_odd_numbers(numbers)
 
# Use OOP to apply the square operation to odd numbers
result = process_numbers(odd_numbers, square_operation_instance.apply_operation)
 
# Display the result
print(result)

Pour un autre exemple d'utilisation conjointe de la POO et de la PF, consultez ce dépôt github.

Conclusion

Il n'y a pas de réponse définitive à la question de savoir quel paradigme de programmation est le meilleur choix, car cela dépend du contexte, de l'objectif, du langage et des préférences du programmeur. Toutefois, il existe quelques lignes directrices générales que vous pouvez suivre pour déterminer le paradigme qui conviendra le mieux à votre situation.

Utilisez la POO lorsque vous devez modéliser des systèmes complexes avec de multiples entités et interactions, et lorsque vous devez encapsuler des données et des comportements dans des composants réutilisables. Utilisez FP lorsque vous devez effectuer des calculs purs avec des entrées et des sorties simples, et lorsque vous devez éviter les effets de bord ou les changements d'état. Utilisez une approche mixte lorsque vous devez tirer parti des atouts des deux paradigmes et lorsque vous devez vous adapter aux caractéristiques et aux limites du langage.

Les meilleurs programmeurs ne sont pas limités à un paradigme ou à un autre ; ils peuvent passer d'un paradigme à l'autre si nécessaire pour atteindre leurs objectifs. Explorez en profondeur une série de paradigmes avec le cours Introduction aux paradigmes de programmation. Essayez de travailler sur un projet de données et utilisez à la fois OOP et FP pour résoudre le projet. En pratiquant chaque paradigme, vous aurez l'intuition de celui qui convient le mieux à votre projet.


Photo of Amberle McKee
Author
Amberle McKee
LinkedIn

Je suis titulaire d'un doctorat et j'ai 13 ans d'expérience dans le traitement des données dans un environnement de recherche biologique. Je crée des logiciels dans plusieurs langages de programmation, notamment Python, MATLAB et R. Je suis passionné par le partage de mon amour de l'apprentissage avec le monde.

Sujets

Commencez dès aujourd'hui votre voyage dans les paradigmes de la programmation !

Certification disponible

cours

Introduction aux paradigmes de programmation

2 hr
1.7K
Explorer un éventail de paradigmes de programmation, y compris la programmation impérative et déclarative, procédurale, fonctionnelle et orientée objet.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow