Cours Complet sur Python

Python est un langage de programmation populaire, reconnu pour sa simplicité, sa lisibilité et sa polyvalence. Il est utilisé dans de nombreux domaines, notamment le développement web, les scripts, le calcul scientifique et l’intelligence artificielle.

Présentation de Python

Python est un langage interprété, orienté objet et de haut niveau. Créé par Guido van Rossum, il est connu pour sa syntaxe simple et intuitive.

# Hello World en Python
print("Hello, World!")

Installation

Vous pouvez télécharger Python sur le site officiel : python.org. Une fois installé, ouvrez votre terminal ou ligne de commande et tapez :

python --version

Vous devriez voir la version de Python installée.

Variables et Types de Données

En Python, les variables sont utilisées pour stocker des informations ou des valeurs de différents types. Python propose plusieurs types de données natifs, dont les plus courants sont :

  • Entiers (int) : pour les nombres entiers (ex : 10)
  • Flottants (float) : pour les nombres décimaux (ex : 3.14)
  • Chaînes de caractères (str) : pour les textes (ex : "Python")
  • Booléens (bool) : pour les valeurs vraies ou fausses (ex : True ou False)

Déclaration et Affectation de Variables

Déclarer une variable en Python est simple : il suffit d'utiliser un nom suivi d'un signe égal (=) et de la valeur souhaitée.

# Exemples de variables
nombre = 10               # Variable entière
pi = 3.14                 # Variable flottante
nom = "Python"            # Variable chaîne de caractères
est_actif = True          # Variable booléenne

Vous pouvez également afficher le type de chaque variable avec la fonction type() :

# Vérifier le type de chaque variable
print(type(nombre))       # 
print(type(pi))           # 
print(type(nom))          # 
print(type(est_actif))    # 

Exemples Avancés : Manipulation des types de données

Conversions de types

Python permet de convertir un type de données en un autre. Voici quelques exemples de conversions :

# Conversion d'un entier en flottant
nombre = 10
flottant = float(nombre)      # Devient 10.0

# Conversion d'un flottant en entier
flottant = 3.14
entier = int(flottant)        # Devient 3

# Conversion d'un entier en chaîne de caractères
entier = 20
chaine = str(entier)          # Devient "20"

# Conversion d'une chaîne de caractères en entier
chaine = "100"
entier = int(chaine)          # Devient 100

Chaînes de Caractères : Manipulations et Opérations

Les chaînes de caractères (ou textes) peuvent être manipulées de différentes façons en Python :

# Exemple de manipulations de chaînes de caractères
message = "Bienvenue en Python!"
print(message.upper())          # Convertir en majuscules
print(message.lower())          # Convertir en minuscules
print(message.replace("Python", "la programmation"))  # Remplacer un mot

Booléens et Opérations Logiques

Les booléens sont souvent utilisés pour contrôler des conditions logiques. Ils peuvent être combinés avec des opérateurs tels que and, or, et not :

# Exemple d'opérations logiques
age = 20
est_majeur = age >= 18          # Retourne True

# Opérations logiques
est_etudiant = True
est_employe = False
resultat = est_etudiant or est_employe  # Retourne True car l'une des conditions est vraie

1. Structure if-else

La structure conditionnelle if permet d'exécuter un bloc de code uniquement si une condition est vraie. L'instruction else s'exécute dans le cas contraire.

Exemple : Vérifions si une personne est majeure ou mineure en fonction de son âge.


# Exemple de structure if-else
age = 20
if age >= 18:
    print("Vous êtes majeur.")
else:
    print("Vous êtes mineur.")
        

2. Structure if-elif-else

Utilisez elif pour vérifier plusieurs conditions successives. Chaque bloc est vérifié dans l'ordre. Cette structure est utile pour les décisions multi-options.

Exemple : Attribution d'une mention en fonction d'un score.


# Exemple de structure if-elif-else
score = 85
if score >= 90:
    print("Mention Excellent")
elif score >= 70:
    print("Mention Bien")
elif score >= 50:
    print("Mention Passable")
else:
    print("Mention Insuffisant")
        

3. Conditions Imbriquées

Les conditions imbriquées permettent de vérifier plusieurs critères dans différents niveaux de logique. Par exemple, on peut vérifier l'âge et le genre d'une personne pour afficher un message spécifique.

Exemple : Message spécifique en fonction de l'âge et du genre.


# Exemple de conditions imbriquées
age = 20
genre = "Femme"
if age >= 18:
    if genre == "Femme":
        print("Vous êtes une femme majeure.")
    else:
        print("Vous êtes un homme majeur.")
else:
    print("Vous êtes mineur.")
        

Boucles

En Python, les boucles permettent de répéter une série d'instructions. for est souvent utilisé pour parcourir une séquence (comme une liste), tandis que while continue tant qu'une condition est vraie.

Boucle for

La boucle for permet de parcourir des séquences, comme des listes, des chaînes, ou des intervalles de nombres.

Exemple : Affichons chaque nombre de 0 à 4 en utilisant range.


# Boucle for avec range
for i in range(5):
    print(i)
            

Boucle while

La boucle while continue tant qu'une condition est vraie. C'est utile pour des itérations où le nombre d'exécutions dépend d'une condition.

Exemple : Affichons les nombres de 0 à 4 en utilisant une boucle while.


# Boucle while
compteur = 0
while compteur < 5:
    print(compteur)
    compteur += 1
            

Boucle for avec une Liste

On peut utiliser une boucle for pour parcourir directement les éléments d'une liste.

Exemple : Affichons chaque fruit dans une liste.


# Boucle for avec une liste
fruits = ["Pomme", "Banane", "Cerise"]
for fruit in fruits:
    print(fruit)
            

Utilisation de break et continue

break termine une boucle prématurément, tandis que continue passe directement à l'itération suivante.

Exemple : Utilisation de break pour interrompre la boucle quand le nombre est égal à 3.


# Boucle avec break
for i in range(5):
    if i == 3:
        break
    print(i)
            

Listes

Les listes sont des collections ordonnées et modifiables, qui permettent de stocker plusieurs éléments. Elles peuvent contenir des types de données variés et des éléments en double.

Création et Manipulation de Listes

Vous pouvez créer une liste en plaçant des éléments entre crochets [] et en les séparant par des virgules.


# Création d'une liste
fruits = ["pomme", "banane", "cerise"]
print(fruits)
        

Ajout d'Éléments

Utilisez append() pour ajouter un élément à la fin de la liste ou insert() pour ajouter un élément à une position spécifique.


fruits.append("orange")     # Ajout à la fin
fruits.insert(1, "mangue")  # Ajout à la position 1
print(fruits)
        

Accès aux Éléments

Les éléments de la liste sont accessibles en utilisant leur index, en commençant par 0 pour le premier élément.


premier_fruit = fruits[0]   # Accès au premier élément
dernier_fruit = fruits[-1]  # Accès au dernier élément
print(premier_fruit, dernier_fruit)
        

Modification d'Éléments

Vous pouvez modifier un élément en accédant à son index et en lui assignant une nouvelle valeur.


fruits[0] = "ananas"   # Remplace "pomme" par "ananas"
print(fruits)
        

Suppression d'Éléments

Utilisez remove() pour supprimer un élément spécifique, pop() pour supprimer par index, ou clear() pour vider la liste.


fruits.remove("banane")  # Supprime "banane"
fruits.pop(2)            # Supprime l'élément à l'index 2
print(fruits)
        

Parcourir une Liste

Utilisez une boucle for pour itérer sur les éléments d'une liste.


for fruit in fruits:
    print(fruit)
        

Listes Imbriquées

Les listes peuvent contenir d'autres listes, permettant de créer des structures multidimensionnelles.


# Liste imbriquée
fruits_et_legumes = [["pomme", "banane"], ["carotte", "épinard"]]
print(fruits_et_legumes[0][1])  # Accès à "banane"
        

Fonctions Utiles pour les Listes

  • len() : retourne le nombre d'éléments dans la liste.
  • sort() : trie la liste en place.
  • reverse() : inverse l'ordre des éléments de la liste.

print(len(fruits))     # Longueur de la liste
fruits.sort()          # Trie la liste
fruits.reverse()       # Inverse la liste
print(fruits)
        

Fonctions Lambda

Les fonctions lambda sont des fonctions anonymes en Python, souvent utilisées pour des calculs simples, des opérations rapides, et en association avec d'autres fonctions comme map, filter, et sorted.

Déclaration de Fonction Lambda

La syntaxe d’une fonction lambda consiste en le mot-clé lambda suivi des paramètres, du symbole :, et de l'expression à évaluer.


# Fonction lambda de base pour calculer le carré
carre = lambda x: x ** 2
print(carre(5))  # Affiche 25
        

Fonction Lambda avec Plusieurs Arguments

Les fonctions lambda peuvent accepter plusieurs arguments.


# Lambda avec deux arguments
addition = lambda x, y: x + y
print(addition(10, 5))  # Affiche 15
        

Utilisation avec map(), filter(), et sorted()

Les fonctions lambda sont souvent utilisées avec des fonctions intégrées pour manipuler des collections de données.

map() avec Lambda

Applique la fonction à chaque élément d’une liste.


# Doubler chaque nombre dans la liste
nombres = [1, 2, 3, 4]
resultat = list(map(lambda x: x * 2, nombres))
print(resultat)  # Affiche [2, 4, 6, 8]
        

filter() avec Lambda

Filtre les éléments d'une liste selon une condition.


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

sorted() avec Lambda

Tri des éléments selon une clé définie par lambda.


# Trier une liste de tuples par le deuxième élément
eleves = [("Alice", 15), ("Bob", 12), ("Charlie", 17)]
eleves_triees = sorted(eleves, key=lambda x: x[1])
print(eleves_triees)  # Affiche [('Bob', 12), ('Alice', 15), ('Charlie', 17)]
        

Fonctions Lambda Imbriquées

Les lambdas peuvent aussi être utilisées comme éléments de retour de fonctions pour des calculs dynamiques.


# Fonction retournant une lambda
def create_multiplier(n):
    return lambda x: x * n

doubler = create_multiplier(2)
tripler = create_multiplier(3)

print(doubler(5))  # Affiche 10
print(tripler(5))  # Affiche 15
        

Classes et Objets

Python est un langage orienté objet, et les classes permettent de structurer le code en utilisant des objets, qui sont des instances de classes. Cela permet de mieux organiser les données et les fonctionnalités associées.

Définition de Base d'une Classe

Une classe est un modèle définissant des propriétés (attributs) et des comportements (méthodes) pour ses objets.


# Définition de la classe Animal
class Animal:
    def __init__(self, nom):  # Constructeur de la classe
        self.nom = nom  # Attribut

    def parler(self):  # Méthode
        print(f"{self.nom} fait du bruit.")

# Création d'une instance de la classe
chien = Animal("Chien")
chien.parler()  # Affiche "Chien fait du bruit."
        

Attributs et Méthodes

Les attributs sont des variables associées à une classe, tandis que les méthodes sont des fonctions définies dans la classe pour agir sur les objets.


class Voiture:
    def __init__(self, marque, annee):
        self.marque = marque
        self.annee = annee

    def demarrer(self):
        print(f"La {self.marque} démarre.")

    def description(self):
        print(f"Voiture : {self.marque}, Année : {self.annee}")

# Création d'un objet Voiture
ma_voiture = Voiture("Toyota", 2022)
ma_voiture.demarrer()  # Affiche "La Toyota démarre."
ma_voiture.description()  # Affiche "Voiture : Toyota, Année : 2022"
        

Héritage

L’héritage permet de créer une nouvelle classe basée sur une classe existante, permettant la réutilisation du code.


# Classe de base
class Animal:
    def __init__(self, nom):
        self.nom = nom

    def parler(self):
        print(f"{self.nom} fait un bruit.")

# Classe dérivée
class Chien(Animal):
    def parler(self):
        print(f"{self.nom} aboie.")

# Création d'une instance de Chien
rex = Chien("Rex")
rex.parler()  # Affiche "Rex aboie."
        

Encapsulation

L'encapsulation est le processus de protection des données en rendant certains attributs privés. En Python, cela se fait en ajoutant un underscore (_) ou un double underscore (__) avant le nom de l'attribut.


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

    def deposer(self, montant):
        self.__solde += montant
        print(f"Dépôt de {montant}. Solde actuel : {self.__solde}")

    def retirer(self, montant):
        if montant <= self.__solde:
            self.__solde -= montant
            print(f"Retrait de {montant}. Solde actuel : {self.__solde}")
        else:
            print("Fonds insuffisants.")

compte = CompteBancaire(1000)
compte.deposer(500)  # Affiche "Dépôt de 500. Solde actuel : 1500"
compte.retirer(200)  # Affiche "Retrait de 200. Solde actuel : 1300"
        

Polymorphisme

Le polymorphisme permet d’utiliser une même méthode pour différents objets, offrant de la flexibilité dans le code.


class Chat:
    def parler(self):
        print("Miaou")

class Chien:
    def parler(self):
        print("Aboiement")

# Fonction qui accepte n'importe quel animal
def faire_parler(animal):
    animal.parler()

# Utilisation du polymorphisme
chat = Chat()
chien = Chien()

faire_parler(chat)  # Affiche "Miaou"
faire_parler(chien)  # Affiche "Aboiement"
        

Les Exceptions

Les exceptions sont des erreurs qui surviennent pendant l'exécution du programme et interrompent son déroulement normal. La gestion des exceptions permet de capturer ces erreurs pour les traiter et éviter que le programme ne s'arrête brutalement.

Gestion de base des exceptions avec try et except

Utilisez try et except pour intercepter les erreurs et exécuter un code alternatif.


# Exemple basique de gestion d'une exception
try:
    resultat = 10 / 0
except ZeroDivisionError:
    print("Erreur : Division par zéro impossible")
        

Gérer plusieurs types d'exceptions

Vous pouvez capturer plusieurs types d'exceptions en utilisant plusieurs blocs except. Par exemple :


try:
    valeur = int(input("Entrez un nombre : "))
    resultat = 10 / valeur
except ValueError:
    print("Erreur : Vous devez entrer un nombre valide.")
except ZeroDivisionError:
    print("Erreur : Division par zéro impossible.")
        

Utilisation de else et finally

Le bloc else est exécuté si aucune exception n'est levée, tandis que le bloc finally est toujours exécuté, que l'exception ait été levée ou non. Cela est utile pour les tâches de nettoyage.


try:
    nombre = int(input("Entrez un nombre : "))
    resultat = 100 / nombre
except ValueError:
    print("Erreur : Entrez un nombre valide.")
except ZeroDivisionError:
    print("Erreur : Division par zéro impossible.")
else:
    print(f"Résultat : {resultat}")
finally:
    print("Opération terminée.")
        

Lever des exceptions personnalisées avec raise

Il est possible de lever une exception manuellement en utilisant raise, notamment pour valider les données ou conditions spécifiques.


def verifier_age(age):
    if age < 18:
        raise ValueError("L'âge doit être supérieur ou égal à 18.")
    return "Âge accepté."

try:
    age = int(input("Entrez votre âge : "))
    print(verifier_age(age))
except ValueError as e:
    print(f"Erreur : {e}")
        

Créer des exceptions personnalisées

Vous pouvez définir vos propres classes d'exceptions pour gérer des erreurs spécifiques de manière plus structurée. Par exemple :


# Définition d'une exception personnalisée
class SoldeInsuffisantError(Exception):
    pass

def retirer_argent(solde, montant):
    if montant > solde:
        raise SoldeInsuffisantError("Le solde est insuffisant pour effectuer cette opération.")
    return solde - montant

try:
    solde = 100
    montant = int(input("Montant à retirer : "))
    nouveau_solde = retirer_argent(solde, montant)
    print(f"Retrait effectué, nouveau solde : {nouveau_solde}")
except SoldeInsuffisantError as e:
    print(f"Erreur : {e}")
        

Décorateurs

Les décorateurs sont des fonctions qui modifient ou enrichissent le comportement d’autres fonctions sans en changer le code. Ils sont souvent utilisés pour ajouter des fonctionnalités comme la journalisation, la vérification d’accès, ou la gestion du temps d’exécution d’une fonction.

Exemple de Décorateur Basique

Dans cet exemple, un décorateur affiche un message avant et après l’exécution de la fonction décorée :


# Définition d'un décorateur de base
def decorateur_simple(fonction):
    def wrapper():
        print("Avant la fonction")
        fonction()
        print("Après la fonction")
    return wrapper

# Application du décorateur avec @
@decorateur_simple
def dire_bonjour():
    print("Bonjour!")

# Appel de la fonction
dire_bonjour()
        

Décorateur avec Arguments

Les décorateurs peuvent aussi accepter des fonctions ayant des paramètres en ajoutant *args et **kwargs dans la fonction wrapper :


# Décorateur qui accepte des arguments de la fonction décorée
def decorateur_args(fonction):
    def wrapper(*args, **kwargs):
        print("Arguments:", args, kwargs)
        return fonction(*args, **kwargs)
    return wrapper

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

# Appel de la fonction avec des arguments
print("Résultat:", addition(3, 5))
        

Utilisation Pratique : Journalisation

Un décorateur peut être utile pour ajouter de la journalisation (log) à une fonction sans modifier son code :


import time

# Décorateur de journalisation
def journalisation(fonction):
    def wrapper(*args, **kwargs):
        print(f"Appel de la fonction '{fonction.__name__}' avec {args} et {kwargs}")
        result = fonction(*args, **kwargs)
        print(f"Résultat: {result}")
        return result
    return wrapper

@journalisation
def multiplier(x, y):
    return x * y

# Appel de la fonction
multiplier(4, 6)
        

Exemple Avancé : Mesure du Temps d'Exécution

Les décorateurs sont souvent utilisés pour mesurer le temps d'exécution d'une fonction, pratique dans les cas où l’optimisation de la performance est importante :


# Décorateur pour mesurer le temps d'exécution
def temps_execution(fonction):
    def wrapper(*args, **kwargs):
        debut = time.time()
        result = fonction(*args, **kwargs)
        fin = time.time()
        print(f"Temps d'exécution de {fonction.__name__}: {fin - debut} secondes")
        return result
    return wrapper

@temps_execution
def somme_lente(n):
    total = 0
    for i in range(n):
        total += i
    return total

# Appel de la fonction avec mesure du temps
somme_lente(1000000)
        

Remarque : Les décorateurs sont un puissant outil pour ajouter des fonctionnalités transversales (comme la journalisation, la vérification de permissions ou la gestion des exceptions) sans altérer le code d'origine des fonctions. Cela améliore la lisibilité et la réutilisabilité du code.