Fonctions, Modules et POO en Python

Natacha Njongwa Yepnga

Objectifs du cours

  • Comprendre les concepts des fonctions en Python : définition, paramètres et valeurs de retour.
  • Explorer les fonctions anonymes (lambda) et leur utilisation pratique.
  • Différencier les variables locales et globales.
  • Maîtriser les listes en compréhension et les dictionnaires en compréhension.
  • Structurer un projet Python avec des modules.
  • Introduire les bases de la Programmation Orientée Objet (POO).

Les fonctions en Python

Les fonctions Python existantes

Python offre une large gamme de fonctions intégrées comme print(), len(), type() ou encore sum(). Ces fonctions permettent de réaliser des opérations courantes sans définir de nouvelles fonctions.

Exemples:

print("Bonjour")  # Affiche : Bonjour
print(len("Python"))  # Affiche : 6

Fonction simple sans paramètre

Une fonction sans paramètre exécute toujours les mêmes instructions :

def dire_bonjour():
    return "Bonjour !"

print(dire_bonjour())  # Affiche : Bonjour !

Fonction avec paramètres

Une fonction avec paramètres peut être personnalisée selon les valeurs passées :

def saluer(nom):
    return f"Bonjour, {nom}!"

print(saluer("Alice"))  # Affiche : Bonjour, Alice!

Valeur par défaut des paramètres

Les paramètres peuvent avoir des valeurs par défaut :

def saluer(nom, message="Bonjour"):
    return f"{message}, {nom}!"

# Exemple d'utilisation
print(saluer("Jean"))  # Affiche : Bonjour, Jean!
print(saluer("Marie", "Salut"))  # Affiche : Salut, Marie!

Affecter une instance de fonction à une variable

Les fonctions peuvent être assignées à des variables pour être utilisées plus tard :

def addition(a, b):
    return a + b

# Affecter la fonction à une variable
ajouter = addition

# Exemple d'utilisation
print(ajouter(3, 4))  # Affiche : 7

Fonction avec un nombre variable de paramètres

Une fonction peut accepter un nombre variable d’arguments :

def somme(*args):
    return sum(args)

# Exemple d'utilisation
print(somme(1, 2, 3, 4))  # Affiche : 10
print(somme(5, 10))       # Affiche : 15

Passage des paramètres : immuable et non immuable

Les types immuables comme les nombres ou les chaînes ne peuvent pas être modifiés dans une fonction, alors que les types mutables comme les listes peuvent l’être.

Exemple :

Avec un type immuable (chaîne) :

def modifier_chaine(chaine):
    chaine += " modifié"
    return chaine

texte = "Texte original"
print(modifier_chaine(texte))  # Affiche : Texte original modifié
print(texte)  # Affiche : Texte original

Avec un type mutable (liste) :

def ajouter_element(liste):
    liste.append(5)

ma_liste = [1, 2, 3]
ajouter_element(ma_liste)
print(ma_liste)  # Affiche : [1, 2, 3, 5]

Variable locale/variable globale

  • Variable locale : Définie à l’intérieur d’une fonction et inaccessible en dehors.
  • Variable globale : Accessible partout dans le programme.

Exemple :

Variables locale :

x = 10  # Variable globale

def afficher_variables():
    y = 5  # Variable locale
    print("x =", x)  # Accède à la variable globale
    print("y =", y)  # Accède à la variable locale

afficher_variables()
# print(y)  # Provoque une erreur : y n'est pas défini en dehors de la fonction

Modifier une variable globale :

z = 20  # Variable globale

def modifier_globale():
    global z  # Permet de modifier la variable globale
    z = 30

modifier_globale()
print(z)  # Affiche : 30

Fonctions anonymes (lambda) et leur usage pratique

Les fonctions anonymes, appelées “lambda” en Python, sont des fonctions définies sans utiliser le mot-clé def. Elles permettent de créer des fonctions courtes, généralement en une seule ligne.

Définir une fonction lambda

Une fonction lambda suit cette syntaxe :

lambda arguments: expression
  • arguments : Les paramètres que la fonction prend.
  • expression : L’opération ou le calcul effectué.

Exemple :

carre = lambda x: x ** 2
print(carre(4))  # Affiche : 16

Utilisation des fonctions lambda

Les fonctions lambda sont souvent utilisées lorsqu’une fonction temporaire ou rapide est nécessaire. Voici quelques cas d’usage pratique :

1. Utilisation avec map()

La fonction map() applique une fonction donnée à chaque élément d’un itérable (comme une liste).

nombres = [1, 2, 3, 4, 5]
carres = list(map(lambda x: x ** 2, nombres))
print(carres)  # Affiche : [1, 4, 9, 16, 25]

2. Utilisation avec filter()

La fonction filter() retourne les éléments d’un itérable qui satisfont une condition.

nombres = [1, 2, 3, 4, 5]
nombres_pairs = list(filter(lambda x: x % 2 == 0, nombres))
print(nombres_pairs)  # Affiche : [2, 4]

3. Utilisation avec sorted()

Les fonctions lambda sont souvent utilisées pour trier des listes selon des critères spécifiques.

nombres = [(1, 2), (3, 1), (5, 0)]
nombres_tries = sorted(nombres, key=lambda x: x[1])
print(nombres_tries)  # Affiche : [(5, 0), (3, 1), (1, 2)]

Comparaison avec les fonctions définies

Les fonctions lambda sont utiles pour des cas simples, mais elles ne remplacent pas les fonctions définies avec def, surtout pour des tâches complexes.

Exemple avec def :

def carre(x):
    return x ** 2

print(carre(4))  # Affiche : 16

Utilisation avancée

1. Combiner lambda avec reduce()

La fonction reduce() applique une fonction de cumul à une séquence, réduisant ainsi cette séquence à une seule valeur.

from functools import reduce

nombres = [1, 2, 3, 4]
somme = reduce(lambda x, y: x + y, nombres)
print(somme)  # Affiche : 10

2. Lambda dans des dictionnaires

Les fonctions lambda peuvent être utilisées dans des dictionnaires pour effectuer des calculs conditionnels.

operations = {
    "addition": lambda x, y: x + y,
    "multiplication": lambda x, y: x * y
}

print(operations["addition"](3, 5))  # Affiche : 8
print(operations["multiplication"](3, 5))  # Affiche : 15

Bonnes pratiques

  1. Lisibilité : N’utilisez pas de lambda pour des fonctions complexes. Préférez une fonction def pour améliorer la lisibilité.
  2. Usage limité : Les fonctions lambda sont conçues pour des cas simples et temporaires.
  3. Documentez : Bien que les lambda soient concises, documentez leur utilisation si elles sont utilisées dans un code complexe.

Exercices pratiques

  1. Calcul de la somme des carrés :
    • Utilisez une fonction lambda avec map() pour calculer le carré de chaque élément d’une liste, puis la somme des carrés avec reduce().
  2. Filtrage :
    • Écrivez une fonction lambda avec filter() pour trouver tous les mots contenant plus de 3 lettres dans une liste de mots.
  3. Tri personnalisé :
    • Créez une liste de tuples représentant des produits avec leur prix. Utilisez une fonction lambda avec sorted() pour trier la liste par prix.

Modules en Python

Introduction aux modules

Un module est un fichier contenant du code Python (fonctions, classes, variables) que vous pouvez réutiliser dans d’autres fichiers. L’utilisation de modules permet de structurer un projet et d’encapsuler des fonctionnalités spécifiques dans des fichiers distincts.

Exemple :
Créez un fichier nommé mon_module.py avec le contenu suivant :

def saluer(nom):
    return f"Bonjour, {nom}!"

Utiliser un module dans un autre fichier

Dans un autre fichier, vous pouvez importer et utiliser ce module :

import mon_module

print(mon_module.saluer("Alice"))  # Affiche : Bonjour, Alice!

Importation de modules

Il existe plusieurs façons d’importer un module ou ses éléments :

  • Importer un module complet :
import math
print(math.sqrt(16))  # Affiche : 4.0
  • Importer un élément spécifique d’un module :
from math import sqrt
print(sqrt(16))  # Affiche : 4.0
  • Renommer un module ou un élément lors de l’importation :
import math as m
print(m.sqrt(16))  # Affiche : 4.0

Modules standards

Python fournit une bibliothèque standard riche avec des modules pour diverses tâches :

  • Manipulation de dates :
import datetime

aujourdhui = datetime.date.today()
print(aujourdhui)  # Affiche la date d'aujourd'hui
  • Génération de nombres aléatoires :
import random

nombre = random.randint(1, 10)
print(nombre)  # Affiche un nombre aléatoire entre 1 et 10

Créer et structurer un projet avec des modules

Un projet Python peut contenir plusieurs fichiers et répertoires pour organiser le code.

Structure d’exemple :

mon_projet/
|-- main.py
|-- utilitaires.py
|-- data/
    |-- __init__.py
    |-- traitement.py

Dans le fichier main.py, vous pouvez importer les modules comme suit :

from data.traitement import ma_fonction

Les packages

Un package est un répertoire contenant plusieurs modules, généralement accompagné d’un fichier __init__.py.

Exemple de structure de package :

mon_package/
|-- __init__.py
|-- module1.py
|-- module2.py

Vous pouvez importer des éléments d’un package comme suit :

from mon_package.module1 import ma_fonction

Introduction à la Programmation Orientée Objet (POO)

La programmation orientée objet (POO) est un paradigme de programmation basé sur le concept de “classes” et “objets”, qui permet de structurer le code de manière modulaire et réutilisable.

Concepts clés

  • Classe : Modèle ou plan permettant de créer des objets. Elle définit les attributs (données) et les méthodes (fonctions).
  • Objet : Instance d’une classe.
  • Attributs : Variables associées à une classe ou un objet.
  • Méthodes : Fonctions définies dans une classe pour manipuler ses attributs ou accomplir des tâches spécifiques.

Créer une classe simple et instancier des objets

Voici un exemple de classe simple représentant une “Personne” :

class Personne:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age

    def se_presenter(self):
        return f"Je m'appelle {self.nom} et j'ai {self.age} ans."

# Instanciation d'objets
personne1 = Personne("Alice", 30)
personne2 = Personne("Bob", 25)

# Utilisation
print(personne1.se_presenter())  # Affiche : Je m'appelle Alice et j'ai 30 ans.
print(personne2.se_presenter())  # Affiche : Je m'appelle Bob et j'ai 25 ans.

Objet complexe

Une classe peut contenir des attributs qui sont eux-mêmes des objets :

class Adresse:
    def __init__(self, rue, ville):
        self.rue = rue
        self.ville = ville

class Personne:
    def __init__(self, nom, age, adresse):
        self.nom = nom
        self.age = age
        self.adresse = adresse

    def afficher_adresse(self):
        return f"{self.adresse.rue}, {self.adresse.ville}"

adresse = Adresse("123 Rue Principale", "Paris")
personne = Personne("Alice", 30, adresse)
print(personne.afficher_adresse())  # Affiche : 123 Rue Principale, Paris

Héritage

L’héritage permet à une classe d’hériter des attributs et des méthodes d’une autre classe. Cela favorise la réutilisation du code.

class Animal:
    def __init__(self, nom):
        self.nom = nom

    def parler(self):
        pass  # Méthode à redéfinir

class Chien(Animal):
    def parler(self):
        return "Woof"

class Chat(Animal):
    def parler(self):
        return "Meow"

chien = Chien("Rex")
chat = Chat("Miaou")
print(chien.parler())  # Affiche : Woof
print(chat.parler())  # Affiche : Meow

Polymorphisme

Le polymorphisme permet d’utiliser une même méthode définie dans plusieurs classes différentes.

animaux = [Chien("Rex"), Chat("Miaou")]

for animal in animaux:
    print(f"{animal.nom} dit {animal.parler()}")

Encapsulation et propriétés

L’encapsulation permet de contrôler l’accès et la modification des attributs d’une classe. Cela se fait généralement à l’aide des propriétés.

class CompteBancaire:
    def __init__(self, titulaire, solde):
        self.titulaire = titulaire
        self.__solde = solde  # Attribut privé

    @property
    def solde(self):
        return self.__solde

    @solde.setter
    def solde(self, montant):
        if montant >= 0:
            self.__solde = montant
        else:
            raise ValueError("Le solde ne peut pas être négatif")

compte = CompteBancaire("Alice", 1000)
print(compte.solde)  # Accès au solde
compte.solde = 2000  # Modification du solde
print(compte.solde)  # Affiche : 2000

Abstraction

L’abstraction consiste à définir des classes ou des méthodes génériques qui servent de base pour les classes spécifiques. Cela se fait avec des classes ou méthodes abstraites.

from abc import ABC, abstractmethod

class Forme(ABC):
    @abstractmethod
    def aire(self):
        pass

class Rectangle(Forme):
    def __init__(self, largeur, hauteur):
        self.largeur = largeur
        self.hauteur = hauteur

    def aire(self):
        return self.largeur * self.hauteur

rectangle = Rectangle(10, 5)
print(rectangle.aire())  # Affiche : 50

Exercices pratiques

  1. Créer une classe Livre :
    • Attributs : titre, auteur, pages.
    • Méthode : afficher_informations qui affiche les détails du livre.
  2. Créer une classe Bibliotheque :
    • Attributs : liste de livres.
    • Méthodes : ajouter un livre, retirer un livre, afficher tous les livres.
  3. Créer une hiérarchie d’héritage :
    • Classe de base Vehicule.
    • Classes dérivées : Voiture, Moto avec des méthodes spécifiques comme afficher_type.
  4. Créer une classe abstraite Forme :
    • Méthodes abstraites : aire et perimetre.
    • Implémenter les classes Cercle et Rectangle.