Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Les bases du langage Python et les notebooks Jupyter

Les bases du langage Python

Dans cette section, je vais vous donner un aperçu des concepts de programmation Python essentiels et des mécanismes du langage.

Sémantique du langage La conception du langage Python se distingue par l’accent mis sur la lisibilité, la simplicité et l’explicite. Certaines personnes vont jusqu’à le comparer à du “pseudocode exécutable”.

Indentation, pas d’accolades

Python utilise des espaces (tabulations ou espaces) pour structurer le code au lieu d’utiliser des accolades comme dans de nombreux autres langages comme R, C++, Java et Perl. Considérez une boucle for d’un algorithme de tri :

nombres = [1, 5, 2, 8, 3]
somme_des_pairs = 0
for num in nombres:
    if num % 2 == 0:
        somme_des_pairs += num
    else:
        print(f"{num} est impair")
1 est impair
5 est impair
3 est impair

Le symbole deux-points (:) signale le début d’un bloc de code indenté. Toutes les lignes de code suivantes appartenant à ce bloc doivent avoir le même niveau d’indentation jusqu’à la fin du bloc. Que cela plaise ou non, l’indentation significative est une caractéristique inhérente à Python. Bien que cela puisse paraître déconcertant au début, on s’y habitue généralement avec la pratique.

Il est fortement conseillé d’utiliser quatre espaces pour l’indentation par défaut et de configurer son éditeur pour que les tabulations soient remplacées par quatre espaces. De nombreux éditeurs de texte offrent cette option (activez-la !). IPython et les notebooks Jupyter gèrent automatiquement l’insertion de quatre espaces sur les nouvelles lignes après un deux-points et convertissent les tabulations en quatre espaces.

Comme vous l’aurez remarqué, les instructions Python ne nécessitent pas de point-virgule en fin de ligne. Toutefois, le point-virgule peut être utilisé pour séparer plusieurs instructions sur une même ligne :

x = 10; y = 20; z = x + y
print(x, y, z)
10 20 30

Placer plusieurs instructions sur une seule ligne est généralement une pratique déconseillée en Python, car cela peut nuire à la lisibilité du code.

Tout est objet

Une spécificité importante de Python réside dans la cohérence de son modèle objet. Chaque élément – nombre, chaîne de caractères, structure de données, fonction, classe, module, etc. – existe au sein de l’interpréteur Python sous la forme d’une “entité” distincte, appelée objet Python. Chaque objet possède un type qui lui est associé (par exemple, entier, chaîne de caractères ou fonction) ainsi que des données internes. En pratique, cela confère au langage une grande souplesse, puisque même les fonctions peuvent être manipulées comme n’importe quel autre objet.

Commentaires Tout texte qui suit un dièse (#) est ignoré par l’interpréteur Python. Cette syntaxe est fréquemment employée pour insérer des commentaires dans le code. Il peut aussi être utile de vouloir désactiver temporairement des blocs de code sans les effacer. Une méthode pour cela est de les commenter :

raw_data = [10, 5, -2, 15, -8, 20]

data_filtree = []
for item in raw_data:
    # Ignorer les valeurs négatives pour le moment
    if item < 0:
      continue
    data_filtree.append(item * 2)
data_filtree
[20, 10, 30, 40]

Les commentaires peuvent également se trouver à la suite d’une ligne de code exécutable. Bien que certains développeurs préfèrent placer les commentaires sur la ligne précédant le code concerné, cette forme peut s’avérer pratique dans certains cas :

x = x + 1 # Incrémenter la valeur
x
11

Appels de fonctions et de méthodes d’objets

On invoque des fonctions en utilisant des parenthèses et en leur passant zéro ou plusieurs arguments. La valeur retournée par la fonction peut éventuellement être assignée à une variable :

# Définir la fonction d'addition
def addition(a, b, c):
  """Ajoute trois nombres ensemble."""
  return a + b + c

# Définir la fonction afficher_message
def afficher_message():
  """Affiche un message simple."""
  print("Le message est affiché.")

val1 = 10
val2 = 20
val3 = 30
somme = addition(val1, val2, val3)
afficher_message()

print(somme) # Facultatif : afficher la somme pour vérifier
Le message est affiché.
60

La quasi-totalité des objets en Python disposent de fonctions associées, nommées méthodes, qui ont la capacité d’accéder aux données internes de l’objet. On les appelle en utilisant la syntaxe suivante :

# Initialiser la liste ma_liste avant de l'utiliser
ma_liste = []
# Définir la variable nouvel_element avant de l'utiliser
nouvel_element = "un nouvel élément" # Vous pouvez remplacer ceci par la valeur que vous souhaitez ajouter

ma_liste.append(nouvel_element)

print(ma_liste)
['un nouvel élément']

Les fonctions peuvent accepter des arguments positionnels ainsi que des arguments par mots-clés :

# Définir la fonction 'calculer'
def calculer(arg1, arg2, option1=False, limite=None):
    """
    Ceci est une fonction d'espace réservé pour 'calculer'.
    Remplacez cela par votre logique réelle.
    """
    print(f"Arguments reçus : arg1={arg1}, arg2={arg2}, option1={option1}, limite={limite}")
    # Ajoutez votre logique de calcul ici
    return arg1 + arg2 # Exemple de valeur de retour

# Assurez-vous que val_x et val_y sont définis avant d'appeler calculer
val_x = 10 # Définir val_x
val_y = 20 # Définir val_y

# Vous pouvez maintenant appeler calculer
sortie = calculer(val_x, val_y, option1=True, limite=100)
Arguments reçus : arg1=10, arg2=20, option1=True, limite=100

Ce point sera examiné plus en détail ultérieurement.

Variables et transmission d’arguments En Python, lorsqu’on assigne une valeur à une variable (ou un nom), on crée en réalité une référence vers l’objet situé à droite du signe égal. Pour illustrer concrètement, prenons une liste d’entiers :

ma_liste_originale = [10, 20, 30]

Supposons que nous assignions ma_liste_originale à une nouvelle variable reference_liste :

reference_liste = ma_liste_originale
reference_liste
[10, 20, 30]

Dans certains langages, l’affectation reference_liste = ma_liste_originale aurait pour effet de dupliquer les données [10, 20, 30]. En Python, reference_liste et ma_liste_originale désignent en fait le même objet, c’est-à-dire la liste initiale [10, 20, 30] (la Figure illustre ce concept). On peut le vérifier en ajoutant un élément à a puis en inspectant b :

referencing in python.png
ma_liste_originale.append(40)
reference_liste
[10, 20, 30, 40]

La compréhension de la sémantique des références en Python – quand, comment et pourquoi les données sont copiées – est absolument cruciale lorsqu’on manipule des ensembles de données volumineux. L’assignation est aussi appelée “liaison” (binding), car on lie un nom à un objet. Les noms de variables ayant fait l’objet d’une assignation sont parfois qualifiés de “variables liées” (bound variables).

Lorsqu’on passe des objets en arguments à une fonction, de nouvelles variables locales sont créées. Celles-ci référencent les objets originaux sans qu’aucune copie ne soit effectuée. Si, à l’intérieur d’une fonction, on lie un nouvel objet à une variable, cela n’affectera pas une variable portant le même nom dans la “portée” extérieure à la fonction (la “portée parente”). Il est donc possible de modifier le contenu interne d’un argument mutable. Prenons la fonction suivante :

def ajouter_valeur(une_liste, valeur):
     une_liste.insert(0, valeur)

Nous avons

ma_sequence = ['a', 'b', 'c']
ajouter_valeur(ma_sequence, 'x')
ma_sequence
['x', 'a', 'b', 'c']

Références dynamiques et typage fort

En Python, les variables n’ont pas de type intrinsèquement associé ; une variable peut désigner un objet de type différent par une simple réassignation. L’exemple suivant ne pose aucun problème :

variable_test = 10.5
print(f"{variable_test}: {type(variable_test)}")
variable_test = True
type(variable_test)
print(f"{variable_test}: {type(variable_test)}")
10.5: <class 'float'>
True: <class 'bool'>

Les variables sont des noms qui identifient des objets au sein d’un espace de noms donné ; l’information de type est conservée dans l’objet lui-même. Certains pourraient en déduire à tort que Python n’est pas un “langage typé”. C’est inexact, comme le montre cet exemple :

10 / "2"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-3c1889a6819d> in <cell line: 0>()
----> 1 10 / "2"

TypeError: unsupported operand type(s) for /: 'int' and 'str'

Dans certains langages, la chaîne de caractères ‘2’ pourrait être convertie implicitement (ou “castée”) en un entier, ce qui donnerait 10. Dans d’autres, l’entier 10 pourrait être transformé en chaîne, résultant en la chaîne concaténée ‘5’. Python n’autorise pas de telles conversions implicites. À cet égard, on dit de Python qu’il est un langage fortement typé : chaque objet a un type (ou une classe) bien défini, et les conversions implicites ne se produisent que dans des cas spécifiques autorisés, comme celui-ci :

num_flottant = 10.0
num_entier = 3
print(f"num_flottant est {type(num_flottant)}, num_entier est {type(num_entier)}")
num_flottant / num_entier
num_flottant est <class 'float'>, num_entier est <class 'int'>
3.3333333333333335

Ici, bien que num_entier soit un entier, il est converti implicitement en nombre à virgule flottante pour l’opération de division.

Il est important de connaître le type d’un objet, et il est pratique de pouvoir écrire des fonctions capables de traiter divers types de données en entrée. La fonction isinstance permet de vérifier si un objet est une instance d’un type particulier :

mon_texte = "Python"
isinstance(mon_texte, str)
True

isinstance peut également prendre un tuple de types si l’on souhaite vérifier si le type d’un objet appartient à l’un des types présents dans le tuple :

val1 = "Bonjour"; val2 = [1, 2]
isinstance(val1, (str, list)) , isinstance(val2, (str, list))
(True, True)

Attributs et méthodes

En Python, les objets possèdent généralement à la fois des attributs (d’autres objets Python “contenus” dans l’objet) et des méthodes (des fonctions associées à un objet, capables d’accéder à ses données internes). On accède aux deux via la syntaxe obj.nom_attribut :

ma_liste_demo = [10, 20]
ma_liste_demo.<Reste ici>

Les attributs et méthodes sont également accessibles par leur nom grâce à la fonction getattr :

ma_liste_demo = [10, 20]
getattr(ma_liste_demo, "append")
<function list.append(object, /)>

Bien que getattr et les fonctions apparentées hasattr et setattr ne soient pas abondamment utilisées dans ce livre, elles se révèlent très utiles pour écrire du code générique et réutilisable.

“Duck Typing” (Typage de canard)

Souvent, le type exact d’un objet importe moins que sa capacité à effectuer certaines actions ou à présenter certains comportements. C’est ce qu’on appelle parfois le “duck typing”, en référence à l’adage : “Si ça marche comme un canard et que ça cancane comme un canard, alors c’est un canard.” Par exemple, on peut déterminer si un objet est itérable en vérifiant s’il implémente le protocole d’itération. Pour de nombreux objets, cela signifie la présence d’une “méthode magique” iter. Cependant, une approche alternative et préférable consiste à essayer d’utiliser la fonction iter :

def peut_etre_indexe(objet):
    try:
        objet[0]
        return True
    except (TypeError, IndexError): # ne peut pas être indexé ou est vide
        return False
peut_etre_indexe("un texte"), peut_etre_indexe([10, 20, 30]), peut_etre_indexe(55)
(True, True, False)

Importations de modules

En Python, un module n’est rien d’autre qu’un fichier portant l’extension .py et contenant du code Python. Imaginons le module suivant :

# mon_utilitaire.py
# CONSTANTE_SALUTATION = "Bonjour"
#
# def saluer(nom):
#   return f"{CONSTANTE_SALUTATION}, {nom}!"
#
# def additionner(x, y):
#   return x + y

Si l’on souhaitait utiliser les variables et fonctions définies dans ou mon_utilitaire.py depuis un autre fichier situé dans le même répertoire, on pourrait procéder ainsi :

import mon_utilitaire
message = mon_utilitaire.saluer("Utilisateur")
constante = mon_utilitaire.CONSTANTE_SALUTATION

Ou bien, de cette manière alternative :

from mon_utilitaire import additionner, CONSTANTE_SALUTATION
resultat = additionner(10, 5)
print(CONSTANTE_SALUTATION)

Le mot-clé as permet d’attribuer des noms différents aux éléments importés :

import mon_utilitaire as util
from mon_utilitaire import CONSTANTE_SALUTATION as salutation, additionner as add

msg = util.saluer("Monde")
res = add(100, util.additionner(5,5)) # Note: util.additionner est aussi disponible

Opérateurs binaires et comparaisons

La majorité des opérations mathématiques binaires et des comparaisons utilisent une syntaxe mathématique conventionnelle, similaire à celle d’autres langages de programmation :

10 * 3, 100 / 4.0, 10 > 2
(30, 25.0, True)
operator.png

Pour déterminer si deux variables font référence au même objet en mémoire, on utilise le mot-clé is. Inversement, is not sert à vérifier que deux objets sont distincts :

x = {"cle": "valeur"}
y = x
z = dict(x)
x is y, x is not z
(True, True)

Puisque la fonction list (ou dict dans cet exemple) génère systématiquement une nouvelle liste (ou un nouveau dictionnaire) Python (c’est-à-dire une copie), on a l’assurance que c (ou z) est différent de a (ou x). Il ne faut pas confondre la comparaison avec is et l’opérateur ==, car dans la situation présente, nous aurions :

x == z
True

Une application fréquente de is et is not est la vérification qu’une variable a la valeur None, étant donné qu’il n’existe qu’une seule instance de None :

ma_var = None
ma_var is None
True
autre_var = "texte"
autre_var is not None
True

Objets modifiables (mutables) et non modifiables (immutables)

De nombreux objets en Python, comme les listes, les dictionnaires, les tableaux NumPy et la plupart des types personnalisés (classes), sont dits modifiables (mutables). Cela implique que l’objet lui-même ou les valeurs qu’il contient peuvent être altérés :

mon_dict = {"nom": "Alice", "age": 30}
mon_dict["age"] = 31
mon_dict
{'nom': 'Alice', 'age': 31}

D’autres, en revanche, tels que les chaînes de caractères et les tuples, sont non modifiables (immutables), ce qui signifie que leurs données internes ne peuvent pas être changées après leur création :

ma_chaine_immutable = "Bonjour"
ma_chaine_immutable[0] = "A"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-40-ecfdaa87b9f8> in <cell line: 0>()
      1 ma_chaine_immutable = "Bonjour"
----> 2 ma_chaine_immutable[0] = "A"

TypeError: 'str' object does not support item assignment

Pour comprendre ce message d’erreur, il faut le lire en partant du bas. La tentative de remplacer le caractère (l’“élément”) à l’index 10 par la lettre “f” a échoué, car cette opération n’est pas permise pour les objets de type chaîne. Si une modification d’une chaîne est nécessaire, il faut utiliser une fonction ou une méthode qui génère une nouvelle chaîne, comme la méthode replace des chaînes :

texte_original = "J'aime Python"
nouveau_texte = texte_original.replace("Python", "Java")
nouveau_texte
Loading...

Suite à cette opération, la variable a (ou texte_original) demeure inchangée :

texte_original
Loading...

De nombreux objets Python peuvent être transformés en leur représentation textuelle grâce à la fonction str :

nombre = 123
chaine_nombre = str(nombre)
print(chaine_nombre + " est un nombre")
123 est un nombre

Les chaînes de caractères sont des séquences de caractères Unicode et peuvent, à ce titre, être manipulées de la même manière que d’autres séquences comme les listes et les tuples :

mot = "program"
tuple(mot)
mot[2:5]
Loading...

La notation mot[2:5] est appelée “slicing” (tranchage) et est disponible pour de nombreux types de séquences en Python. Ce concept sera exploré plus avant ultérieurement, car son usage est fréquent dans cet ouvrage.

Le caractère antislash \ sert de caractère d’échappement ; il est utilisé pour désigner des caractères spéciaux tels que le saut de ligne \n ou des caractères Unicode. Pour inclure des antislashs littéraux dans une chaîne, il faut les échapper eux-mêmes :

chemin = "C:\\Utilisateurs\\Nom"
print(chemin)
C:\Utilisateurs\Nom

Si une chaîne contient de nombreux antislashs et aucun caractère spécial, cette nécessité d’échappement peut devenir fastidieuse. Heureusement, on peut préfixer la chaîne (avant le premier guillemet) par la lettre r, indiquant que les caractères doivent être interprétés littéralement (raw string) :

import re

# Modèle d’expression régulière pour les numéros de téléphone camerounais :
#  - (?:\+237|237)?   : préfixe international facultatif "+237" ou "237"
#  - [\s\-]*          : espaces ou tirets facultatifs
#  - [26]\d{2}        : commence par 2xx (fixe) ou 6xx (mobile)
#  - (?:[\s\-]*\d{3}){2} : deux groupes de trois chiffres, séparateurs facultatifs
pattern = re.compile(r"(?:\+237|237)?[\s\-]*[26]\d{2}(?:[\s\-]*\d{3}){2}")

# Grand texte d’exemple avec divers formats de numéros camerounais
text = """
Bonjour, vous pouvez me joindre au +237 650 123 456 pour toute question commerciale.
Pour le service client appelez le 237612345678 ou le 612-345-678 directement.
En cas d’urgence, composez simplement 699 000 111 ou même +237699000111.
Notre bureau à Douala est joignable au 233.444.555 (pas de code international nécessaire).
Le support technique 237 233 444 666 reste disponible 24/7.
Si vous préférez, envoyez un SMS au 650-321-123 ou au +237 650321123.
Pour les VIP : +237-677-888-999, 677 888 000, et aussi 237699999000.
Notez aussi ces anciens numéros : 233333444, +237 233 333 444, et 233-333-444.
Enfin, notre hotline est le 660 111 222 ou bien +237660111222.
"""

# Recherche de tous les numéros au format camerounais
found_numbers = pattern.findall(text)

print("Numéros camerounais extraits :")
for num in found_numbers:
    clean = re.sub(r"[\s\-\.]+", "", num)  # on supprime espaces, tirets, points
    print(f"  {num}  →  {clean}")
Numéros camerounais extraits :
  +237 650 123 456  →  +237650123456
   237612345  →  237612345
   612-345-678  →  612345678
   699 000 111  →  699000111
  +237699000111  →  +237699000111
   237 233 444  →  237233444
   650-321-123  →  650321123
  +237 650321123  →  +237650321123
  +237-677-888-999  →  +237677888999
   677 888 000  →  677888000
   237699999  →  237699999
   233333444  →  233333444
  +237 233 333 444  →  +237233333444
   233-333-444  →  233333444
   660 111 222  →  660111222
  +237660111222  →  +237660111222

Le r est l’abréviation de “raw” (brut).

La concaténation de deux chaînes de caractères s’effectue par l’opérateur +, ce qui produit une nouvelle chaîne :

prenom = "Jean"
nom = " Dupont"
nom_complet = prenom + nom
nom_complet
Loading...

La mise en forme de chaînes, ou “templating”, est un autre sujet d’importance. Les techniques pour ce faire se sont diversifiées avec l’arrivée de Python 3. Je vais ici brièvement exposer le fonctionnement de l’une des interfaces principales. Les objets de type chaîne possèdent une méthode format qui permet de substituer des arguments formatés au sein de la chaîne, générant ainsi une nouvelle chaîne :

modele = "Nom: {nom}, Age: {age:d}"
modele.format(nom="Alice", age=30)
Loading...

Python 3.6 a introduit les “f-strings” (littéraux de chaînes formatées), une fonctionnalité qui simplifie davantage la création de chaînes formatées. Pour créer une f-string, il suffit de faire précéder le littéral de chaîne du caractère f. À l’intérieur de la chaîne, les expressions Python encadrées par des accolades seront remplacées par leur valeur dans la chaîne finale :

quantite = 5
prix_unitaire = 12.5
produit = "Pommes"
message = f"Vous avez {quantite} {produit} pour un total de {quantite * prix_unitaire} EUR."
message
Loading...

Des spécificateurs de format peuvent être ajoutés après chaque expression, en utilisant la même syntaxe que pour les modèles de chaînes vus précédemment :

f"Total: {quantite * prix_unitaire:.2f} EUR"
Loading...

Le formatage de chaînes est un domaine vaste ; il existe diverses méthodes et de nombreuses options pour contrôler finement la manière dont les valeurs sont intégrées dans la chaîne résultante. Pour une exploration approfondie, référez-vous à la documentation officielle de Python.

Octets (Bytes) et Unicode

Dans les versions modernes de Python (Python 3.0 et ultérieures), Unicode est devenu le standard pour les chaînes de caractères, permettant une gestion plus homogène des textes ASCII et non-ASCII. Dans les anciennes versions de Python, les chaînes étaient nativement des séquences d’octets sans encodage Unicode explicite. La conversion vers Unicode était possible à condition de connaître l’encodage des caractères d’origine. Voici un exemple de chaîne Unicode contenant des caractères non-ASCII :

mot_jp = "日本語"
mot_jp
Loading...

On peut convertir cette chaîne Unicode en sa représentation en octets au format UTF-8 grâce à la méthode encode :

mot_jp_utf8 = mot_jp.encode("utf-8")
mot_jp_utf8, type(mot_jp_utf8)
(b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e', bytes)

Si l’on connaît l’encodage Unicode d’un objet de type bytes, on peut le reconvertir en chaîne Unicode avec la méthode decode :

mot_jp_utf8.decode("utf-8")
Loading...

Bien que l’UTF-8 soit désormais l’encodage à privilégier pour toute nouvelle donnée, pour des raisons historiques, on peut être amené à traiter des données encodées avec divers autres standards :

salut = "你好"
salut.encode("gbk")
b'\xc4\xe3\xba\xc3'
salut.encode("utf-16")
salut.encode("utf-16le")
b'`O}Y'

Il est fréquent de manipuler des objets de type bytes lors de la lecture de fichiers, situation où un décodage implicite de toutes les données en chaînes Unicode n’est pas toujours souhaitable.

Booléens

En Python, les deux valeurs booléennes s’écrivent True et False. Les comparaisons et autres expressions conditionnelles s’évaluent à True ou False. On combine les valeurs booléennes à l’aide des mots-clés and et or :

(5 > 3) and (1 < 2), (1 == 0) or ("a" == "a")
(True, True)

Lorsqu’ils sont convertis en nombres, False équivaut à 0 et True à 1 :

float(True), float(False)
(1.0, 0.0)

Le mot-clé not inverse la valeur d’un booléen, transformant True en False et vice-versa :

est_valide = (10 > 5)
est_invalide = (0 == 1)
not est_valide, not est_invalide
(False, True)

Conversion de type (Casting)

Les types str, bool, int et float servent également de fonctions pour convertir des valeurs vers ces types respectifs :

nombre_chaine = "100"
entier_val = int(nombre_chaine)
type(entier_val), float(entier_val), bool(entier_val), bool("")
(int, 100.0, True, False)
type(None), bool(None)
(NoneType, False)
0 == None
False

La valeur None

None représente la valeur nulle en Python :

ma_variable_nulle = None
ma_variable_nulle is None
True
une_autre_variable = [1,2]
une_autre_variable is not None
True

None est aussi fréquemment utilisé comme valeur par défaut pour les arguments de fonctions :

def formater_texte(texte, prefixe=None):
    if prefixe is not None:
        return prefixe + ": " + texte
    return texte
# Cas d'utilisation 1 : Ajout d'une étiquette
message = "Erreur lors de l'enregistrement du fichier."
message_etiquette = formater_texte(message, prefixe="ERREUR")
print(message_etiquette)

message = "Opération réussie."
message_etiquette = formater_texte(message, prefixe="SUCCÈS")
print(message_etiquette)

message = "Information importante à retenir."
message_etiquette = formater_texte(message, prefixe="INFO")
print(message_etiquette)
ERREUR: Erreur lors de l'enregistrement du fichier.
SUCCÈS: Opération réussie.
INFO: Information importante à retenir.
# Cas d'utilisation 2 : Génération de messages de journalisation
def log_message(level, message):
    print(formater_texte(message, prefixe=level.upper()))

log_message("info", "Application démarrée.")
log_message("warning", "Espace disque faible.")
log_message("error", "Échec de la connexion à la base de données.")
INFO: Application démarrée.
WARNING: Espace disque faible.
ERROR: Échec de la connexion à la base de données.
# Cas d'utilisation 3 : Formatage de la sortie pour différentes sections
donnees_utilisateur = {"name": "Alice", "email": "alice@example.com"}
informations_produit = {"id": "P101", "price": 50.0}

print(formater_texte(f"Nom : {donnees_utilisateur['name']}, E-mail : {donnees_utilisateur['email']}", prefixe="Utilisateur"))
print(formater_texte(f"ID : {informations_produit['id']}, Prix : {informations_produit['price']}", prefixe="Produit"))
Utilisateur: Nom : Alice, E-mail : alice@example.com
Produit: ID : P101, Prix : 50.0
# Cas d'utilisation 4 : Gestion des préfixes facultatifs
texte_simple = "Ceci est un message normal."
texte_simple_formate = formater_texte(texte_simple)
print(texte_simple_formate)

texte_prefixe = "Ce message a un préfixe."
texte_prefixe_formate = formater_texte(texte_prefixe, prefixe="NOTE")
print(texte_prefixe_formate)
Ceci est un message normal.
NOTE: Ce message a un préfixe.

Dates et heures

Le module natif datetime de Python met à disposition les types datetime, date et time. Le type datetime, qui fusionne les informations de date et time, est le plus utilisé :

from datetime import datetime
maintenant = datetime.now() # Obtient la date et l'heure actuelles
maintenant.year, maintenant.hour, maintenant.minute, maintenant.second, maintenant.microsecond
(2025, 17, 45, 52, 827121)

À partir d’une instance datetime, on peut en extraire les objets date et time correspondants en appelant les méthodes du même nom sur l’objet datetime :

from datetime import date

maintenant.date()
date(2024, 5, 17)
maintenant.time()
datetime.time(17, 45, 52, 827121)

La méthode strftime permet de formater un objet datetime en une chaîne de caractères :

maintenant.strftime("%A, %d %B %Y - %I:%M %p")
Loading...
help(date.strftime)
Help on method_descriptor:

strftime(...) unbound datetime.date method
    format -> strftime() style string.

Inversement, des chaînes de caractères peuvent être converties (analysées) en objets datetime à l’aide de la fonction strptime :

date_chaine = "01/01/2025"
datetime.strptime(date_chaine, "%d/%m/%Y")
datetime.datetime(2025, 1, 1, 0, 0)

Lors de l’agrégation ou du regroupement de données temporelles, il est parfois utile de modifier certains champs horaires d’une série d’objets datetime, par exemple, en réinitialisant les minutes et les secondes à zéro :

dt_specifique = datetime(2023, 11, 20, 10, 45, 30)
dt_debut_jour = dt_specifique.replace(hour=0, minute=0, second=0, microsecond=0)
dt_debut_jour
datetime.datetime(2023, 11, 20, 0, 0)

Puisque datetime.datetime est un type immutable, les méthodes de ce genre retournent toujours de nouveaux objets. Ainsi, dans l’exemple précédent, dt (ou dt_specifique) n’est pas altéré par l’appel à replace :

dt_specifique
datetime(2023, 11, 20, 10, 45, 30)
datetime.datetime(2023, 11, 20, 10, 45, 30)

La soustraction de deux objets datetime produit un objet de type datetime.timedelta :

date_debut = datetime(2024, 1, 1)
date_fin = datetime(2024, 1, 10, 12, 0)
difference = date_fin - date_debut
difference, type(difference)
(datetime.timedelta(days=9, seconds=43200), datetime.timedelta)

La sortie (datetime.timedelta(days=9, seconds=43200) signifie que l’objet timedelta représente un intervalle de 9 jours et 43200 secondes. L’ajout d’un timedelta à un datetime donne un nouvel objet datetime décalé :

date_debut
date_debut + difference
datetime.datetime(2024, 1, 10, 12, 0)

Structures de contrôle du flux

Python intègre plusieurs mots-clés pour la logique conditionnelle, les boucles et d’autres concepts de contrôle de flux que l’on retrouve dans d’autres langages de programmation.

Instructions if, elif et else

L’instruction if est l’une des structures de contrôle de flux les plus répandues. Elle évalue une condition et, si celle-ci est vraie (True), exécute le bloc de code qui la suit :

temperature = 25
if temperature > 20:
    print("Il fait chaud")
Il fait chaud

Une instruction if peut être suivie, optionnellement, d’une ou plusieurs clauses elif et d’une clause else finale qui s’exécute si toutes les conditions précédentes sont fausses (False) :

note = 75
if note >= 90:
    print("A")
elif note >= 80:
    print("B")
elif note >= 70:
    print("C")
else:
    print("Échec")
C

Si l’une des conditions (if ou elif) est vérifiée, les blocs elif ou else suivants ne sont pas évalués. Dans le cas d’une condition composite utilisant and ou or, l’évaluation se fait de gauche à droite et s’interrompt dès que le résultat est connu (évaluation paresseuse) :

stock, commande, priorite, special = 10, 5, True, False
if stock > commande and (priorite or special):
     print("Expédier la commande")
Expédier la commande

Il est également possible d’enchaîner les comparaisons :

10 < 20 <= 20 < 30
True

Boucles for

Les boucles for sont utilisées pour parcourir les éléments d’une collection (comme une liste ou un tuple) ou d’un itérateur. La syntaxe typique d’une boucle for est :

fruits = ["pomme", "banane", "cerise"]
for fruit in fruits:
    print(f"J'aime les {fruit}s")
J'aime les pommes
J'aime les bananes
J'aime les cerises

On peut passer à l’itération suivante d’une boucle for, en ignorant le reste du bloc de code pour l’itération courante, grâce au mot-clé continue.

nombres_mixtes = [10, -5, 0, 20, -15, 8]
somme_positive = 0
for num in nombres_mixtes:
    if num <= 0:
        continue
    somme_positive += num
print(f"Somme des positifs: {somme_positive}")
Somme des positifs: 38

Une boucle for peut être interrompue prématurément avec le mot-clé break. Le code ci-dessous additionne les éléments d’une liste jusqu’à ce qu’il rencontre la valeur 5 :

articles_a_traiter = ["email1", "email2", "spam", "email3"]
traites_avec_succes = 0
for article in articles_a_traiter:
    if article == "spam":
        print("Spam détecté, arrêt.")
        break
    print(f"Traitement de {article}")
    traites_avec_succes +=1
print(f"{traites_avec_succes} articles traités.")
Traitement de email1
Traitement de email2
Spam détecté, arrêt.
2 articles traités.

Le mot-clé break met fin uniquement à la boucle for la plus interne ; toute boucle for englobante poursuit son exécution :

 for x in range(1, 3): # Boucle externe: 1, 2
     print(f"Cycle externe x={x}")
     for y in "ab": # Boucle interne: 'a', 'b'
         if x == 1 and y == 'b':
             print("  Break interne pour x=1, y=b")
             break
         print(f"  Interne y={y}")
Cycle externe x=1
  Interne y=a
  Break interne pour x=1, y=b
Cycle externe x=2
  Interne y=a
  Interne y=b

Comme nous le verrons plus en détail, si les éléments de la collection ou de l’itérateur sont eux-mêmes des séquences (des tuples ou des listes, par exemple), il est aisé de les décomposer en variables distinctes directement dans l’instruction de la boucle for

points = [(1, 2), (3, 4), (5, 6)]
for x_coord, y_coord in points:
    print(f"x: {x_coord}, y: {y_coord}")
x: 1, y: 2
x: 3, y: 4
x: 5, y: 6

Boucles while

Une boucle while définit une condition et un bloc de code qui s’exécute tant que la condition reste vraie (True), ou jusqu’à ce que la boucle soit explicitement terminée par une instruction break :

compteur = 5
while compteur > 0:
    print(f"Compte à rebours: {compteur}")
    compteur -= 1
    if compteur == 2:
        print("Pause à 2!")
        # break # Décommenter pour un break
print("Décollage!")
Compte à rebours: 5
Compte à rebours: 4
Compte à rebours: 3
Pause à 2!
Compte à rebours: 2
Compte à rebours: 1
Décollage!

L’instruction pass

L’instruction pass est une opération neutre en Python (elle ne fait rien). Elle peut être employée dans des blocs où aucune action n’est requise (ou comme marqueur pour du code à implémenter ultérieurement). Sa présence est nécessaire uniquement parce que Python utilise l’indentation pour délimiter les blocs :

statut_utilisateur = "inactif"
if statut_utilisateur == "actif":
    print("Utilisateur actif.")
elif statut_utilisateur == "inactif":
    # Action future pour les utilisateurs inactifs
    pass
else:
    print("Statut inconnu.")

La fonction range

La fonction range produit une séquence d’entiers régulièrement espacés :

range(5), tuple(range(5))
(range(0, 5), (0, 1, 2, 3, 4))

On peut spécifier un point de départ, un point d’arrivée et un pas (qui peut être négatif) :

list(range(1, 11, 3)) # Commence à 1, jusqu'à 10 (exclu), par pas de 3
[1, 4, 7, 10]
list(range(10, 0, -2)) # De 10 à 1 (exclu), par pas de -2
[10, 8, 6, 4, 2]

Comme on peut le constater, range génère des entiers jusqu’à la valeur de fin, cette dernière étant exclue. Une utilisation fréquente de range est l’itération sur les indices d’une séquence :

couleurs = ["rouge", "vert", "bleu"]
for idx in range(len(couleurs)):
    print(f"Couleur à l'index {idx} est {couleurs[idx]}")
Couleur à l'index 0 est rouge
Couleur à l'index 1 est vert
Couleur à l'index 2 est bleu

Bien qu’il soit possible d’utiliser des fonctions comme list pour stocker tous les entiers produits par range dans une autre structure de données, c’est souvent la forme itérative par défaut qui est la plus appropriée. L’extrait de code suivant calcule la somme de tous les nombres de 0 à 99 999 qui sont multiples de 3 ou de 5 :

 compte_multiples_sept = 0
 for num in range(1, 50): # Nombres de 1 à 49
     if num % 7 == 0:
         compte_multiples_sept += 1

 print(f"Nombre de multiples de 7 entre 1 et 49: {compte_multiples_sept}")
Nombre de multiples de 7 entre 1 et 49: 7

Même si la plage de nombres générée peut être très étendue, la consommation de mémoire à un instant donné reste très faible.