Sommaire
Dans cette partie, on va aborder les opérateurs, les conditions et les boucles. Enfin, nous verrons quelques mots clés.
Rien de particulier ici:
< inférieur à<= inférieur ou égale à> supérieur à>= supérieur ou égale à== égalité!= différentConcernant les chaînes de caractères:
"hello" == "hello" # True
"hello" == "world" # False
"hello" >= "hello" # True
"auth" >= "world" # True - compare la première lettre - ordre lexicographique (table ASCII)
Au nombre de trois: and, or et not .
L'ordre de priorité est le suivant: not, and et or .
| a | b | a and b |
|---|---|---|
True |
True |
True |
True |
False |
False |
False |
False |
False |
False |
True |
False |
| a | b | a or b |
|---|---|---|
True |
True |
True |
True |
False |
True |
False |
True |
True |
False |
False |
False |
| a | not a |
|---|---|
True |
False |
False |
True |
SIRappel: Comme expliqué dans les fondamentaux, les indentations en Python ont une signification et indique un 'block' de code. C'est l'équivalent des { } en php.
# instruction SI
if condition:
...
# instruction SI ... SINON
if condition:
...
else:
...
# instruction SI ... SINON SI ... SINON ...
if condition:
...
elif condition:
...
else:
...
Le sucre syntaxique n'est pas aussi sexy qu'en php ici (pas de ?? non plus).
Toutefois, voici l'opérateur ternaire en Python:
# opérateur ternaire
valeur_si_vrai if condition else valeur_si_faux
# exemple
age = 20
message = "Majeur" if age >= 18 else "Mineur"
print(message) # Affiche "Majeur"
Le Python ne dispose pas de switch comme en php. Par contre, il existe un match ... case, qui est "similaire" au match de php.
# exemple de match
def choix_action(action):
match action:
case "start":
return "Démarrage en cours..."
case "stop":
return "Arrêt en cours..."
case "pause":
return "Pause activée..."
case _:
return "Action inconnue !"
print(choix_action("pause")) # Affiche "Pause activée..."
Python, utilise une combinaison de for et de la fonction range(). On retrouve également la traditionnelle boucle while.
range([start], stop, [step]) permet de générer une séquence de nombre:
start optionnel: valeur de départ (par défaut 0)stop obligatoire: valeur d'arrêtstep optionnel: le pas (par défaut 1)for, on obtient la boucle for:
# exemple de boucle for
for i in range(5): # Equivalent à range(0, 5, 1)
print(i)
# affiche 0,1,2,3,4
La boucle universelle.
# syntaxe de la boucle while
while condition:
# Code à exécuter
#exemple
i = 1
while i <= 5:
print(i)
i += 1
# i vaut à chaque tour de boucle : 1,2,3,4,5,6
# arrivé à 6 la boucle s'arrête
En Python, il n'existe pas de structure native « do…while » comme en JavaScript ou PHP. Cependant, on peut en simuler le comportement de manière condensée en utilisant une boucle infinie avec une condition de sortie placée après le bloc d'instructions.
# Syntaxe simulée do…while
while True:
# Bloc d'instructions à exécuter (exécuté au moins une fois)
# ...
# Vérification de la condition après l'exécution du bloc
if condition_de_sortie:
break
# Exemple simple
i = 0
while True:
print(i)
i += 1
if i >= 3: # Condition de sortie, simulant "faire... jusqu'à"
break
while True garantit que le bloc s'exécute au moins une fois.if i >= 3) est vérifiée à la fin de chaque itération.break interrompt la boucle, simulant ainsi le comportement d'un do…while.En Python, vous pouvez associer un bloc else à une boucle for (ou à une boucle while). Le bloc else s'exécute uniquement lorsque la boucle se termine normalement, c'est-à-dire lorsque l'itération sur l'objet (liste, chaîne, etc.) est complètement effectuée, sans que l'instruction break ait été rencontrée. Autrement dit, si l'exécution de la boucle est interrompue prématurément par un break, le code contenu dans le else ne sera pas exécuté.
Syntaxe:
for élément in collection:
# instructions pour chaque élément
if condition_de_sortie:
break
else:
# instructions à exécuter si aucun break n'a été rencontré
Quelques points importants :
else doit être indenté au même niveau que le for.break.L'un des usages les plus courants du for ... else est dans la recherche d'un élément qui satisfait une condition. Plutôt que d'utiliser une variable booléenne pour savoir si l'élément a été trouvé, le bloc else gère automatiquement le cas « pas trouvé » après l'itération.
# Exemple avec une recherche dans une chaîne de caractères
mot = "bonjour"
recherche = "z"
for lettre in mot:
if lettre == recherche:
print("Lettre trouvée!")
break
else:
print("Lettre non trouvée dans le mot.")
Si vous avez besoin de vérifier qu'un ensemble d'éléments respecte une condition et de réagir différemment en fonction de la présence d'une exception (via break), le for ... else vous permet de bien séparer ces deux cas.
break.else est associé directement à la boucle et non au if.for ... else puisse prêter à confusion, il s'avère très élégant pour les cas de recherche ou de vérification intégrale dans une collection.for, ajoutez un commentaire si nécessaire pour expliquer son comportement dans le contexte de votre code.break se comporte comme attendu pour éviter d'exécuter le bloc else par inadvertance.La boucle for ... else en Python est un outil puissant pour gérer des cas de boucles où la complétion normale (sans interruption) doit déclencher une action spécifique. Que ce soit pour la recherche d'un élément ou la validation complète d'un critère, cette syntaxe offre une solution élégante et lisible dès lors qu'elle est bien documentée dans votre code.
La boucle while s'exécute tant qu'une condition donnée est vraie. On peut ajouter une clause else à cette boucle. La particularité est la suivante:
else s'exécute uniquement si la boucle while se termine "normalement", c'est-à-dire lorsque la condition devient fausse.break, le bloc else n'est pas exécuté.while condition:
# bloc d'instructions exécuté tant que condition est vraie
if condition_de_sortie:
break
# d'autres instructions
else:
# bloc d'instructions exécuté si la boucle s'est terminée sans rencontrer de break
Points clés à retenir :
while vérifie la condition avant chaque itération.break est utilisé pour interrompre la boucle prématurément.>else permet de traiter la situation où la condition n'a plus de raison de rester vraie (fin normale de la boucle).Recherche d'un élément dans une séquence
Imaginons que nous voulons parcourir une liste pour chercher si un élément particulier y figure. Si l'élément est trouvé, on interrompt la boucle avec un break. Sinon, si la boucle se termine sans interruption, le bloc else nous informe que l'élément n'existe pas dans la séquence.
# Exemple sans trouver l'élément
valeurs = [2, 4, 6, 8]
i = 0
while i < len(valeurs):
# On cherche un nombre impair
if valeurs[i] % 2 != 0:
print(f"Nombre impair trouvé : {valeurs[i]}")
break
i += 1
else:
print("Aucun nombre impair n'a été trouvé dans la liste.")
Explications:
while parcourt les indices de la liste tant que i est inférieur à la longueur de la liste.if n'est jamais satisfaite et le break n'est jamais exécuté.else s'exécute et affiche que aucun nombre impair n'a été trouvé.# Exemple en trouvant l'élément
valeurs = [2, 4, 5, 8]
i = 0
while i < len(valeurs):
# On recherche un nombre impair
if valeurs[i] % 2 != 0:
print(f"Nombre impair trouvé : {valeurs[i]}")
break
i += 1
else:
print("Aucun nombre impair n'a été trouvé dans la liste.")
Explications:
i atteint l'indice correspondant à 5, la condition est vraie, le message est affiché et le break interrompt la boucle.break est exécuté, le bloc else n'est pas atteint.Le couple while ... else est très utile dans des situations de recherche ou de validation de conditions sur une collection d'éléments. Il permet de distinguer clairement le cas où :
break).else).L'utilisation de else avec les boucles permet de simplifier la gestion d'un cas particulier. Au lieu de devoir utiliser une variable flag pour déterminer si une interruption par break est intervenue, la syntaxe intégrée assure que l'action voulue (traiter le cas "non trouvé") est clairement associée à la fin normale de la boucle.
Si la condition du while est fausse dès le début, le bloc while ne s'exécute même pas, et le bloc else s'exécute immédiatement. Cela permet de gérer automatiquement le cas d'une collection vide ou d'une situation où l'état initial n'exige pas d'itération.
Exemple:
nombre = 0
while nombre > 0:
# Cette boucle ne s'exécutera pas car la condition est déjà fausse
print("Cette instruction ne sera jamais affichée.")
nombre -= 1
else:
print("La boucle while ne s'est jamais exécutée, bloc else exécuté.")
Bonnes pratiques:
else avec la boucle while n'est pas évident pour tous les développeurs. Ajoutez des commentaires pour clarifier son rôle et éviter toute confusion.break et dans le cas d'une fin "naturelle" de la boucle.La structure while ... else en Python permet de différencier clairement la fin naturelle d'une boucle de son interruption par un break. C'est un outil très utile pour améliorer la clarté du code, notamment dans des scénarios de recherche ou de validation d'éléments dans une séquence. Bien que moins utilisé que la boucle for ... else, il s'agit d'une fonctionnalité puissante qui, une fois bien comprise, permet d'écrire un code plus lisible et mieux structuré.
En Python, un itérable (iterable) est tout objet sur lequel on peut itérer, c'est-à-dire parcourir ses éléments un par un. Cela inclut des objets comme les listes, les tuples, les chaînes de caractères, les dictionnaires, etc. (voir les structures de données). Concrètement, un objet est itérable s'il implémente la méthode spéciale __iter__(), qui renvoie un itérateur.
Un itérateur (iterator), de son côté, est un objet qui représente une séquence de données et qui permet d'accéder à ses éléments un à un, généralement à l'aide de la méthode __next__(). Lorsqu'on appelle __next__(), l'itérateur renvoie l'élément suivant de la séquence. Lorsque la séquence est épuisée, un appel à __next__() déclenche l'exception StopIteration.
# Un itérable : une liste
ma_liste = [1, 2, 3, 4]
# Obtenir un itérateur à partir de l'itérable
iterateur = iter(ma_liste)
# Utilisation de l'itérateur avec la fonction next()
print(next(iterateur)) # Affiche 1
print(next(iterateur)) # Affiche 2
print(next(iterateur)) # Affiche 3
print(next(iterateur)) # Affiche 4
# La prochaine instruction lèvera une exception StopIteration
# print(next(iterateur))
En pratique, les boucles for utilisent ces concepts en interne pour parcourir automatiquement les éléments d'un itérable :
for element in ma_liste:
print(element)
Ici, Python appelle implicitement iter(ma_liste) pour obtenir un itérateur, puis utilise next() à chaque itération jusqu'à ce que l'itérateur soit épuisé.
Pourquoi ces concepts sont-ils utiles ?
__iter__() et __next__(), ce qui est très utile pour des besoins spécifiques de parcours de données.La fonction intégrée map() permet d'appliquer une fonction à chaque élément d'un ou plusieurs itérables (comme des listes, tuples, etc.) et retourne un itérateur contenant les résultats. Elle est particulièrement utile pour transformer rapidement des collections de données sans avoir à écrire de boucles explicites. Cette approche est souvent plus concise et peut améliorer la lisibilité du code.
La syntaxe générale de map() est :
map(fonction, itérable, ...)
map() est un objet de type map, qui est un itérateur. Pour obtenir une liste ou un tuple des résultats, il faut le convertir explicitement avec list() ou tuple().
Application d'une fonction simple:
def carre(nombre):
return nombre ** 2
nombres = [1, 2, 3, 4, 5]
resultat = map(carre, nombres)
# Conversion de l'objet map en liste pour afficher le résultat
print(list(resultat)) # Affiche : [1, 4, 9, 16, 25]
Ici, la fonction carre est appliquée à chaque élément de la liste nombres.
Utilisation avec une fonction lambda.
Pour des opérations simples, on peut utiliser une fonction lambda directement dans map():
nombres = [1, 2, 3, 4, 5]
resultat = map(lambda x: x ** 2, nombres)
print(list(resultat)) # Affiche : [1, 4, 9, 16, 25]
L'utilisation d'une lambda permet d'écrire le code de manière plus concise, surtout pour des transformations simples.
Traitement de plusieurs itérables.
map() peut aussi travailler sur plusieurs itérables simultanément. Par exemple, additionner les éléments correspondants de deux listes :
liste1 = [1, 2, 3]
liste2 = [4, 5, 6]
resultat = map(lambda x, y: x + y, liste1, liste2)
print(list(resultat)) # Affiche : [5, 7, 9]
Dans ce cas, la lambda reçoit deux arguments (un de chaque liste) et renvoie leur somme.
Points importants:
map() est un itérateur, ce qui signifie que les valeurs ne sont calculées que lorsque vous itérez dessus. Cela permet d'économiser de la mémoire, surtout lorsqu'on travaille avec de grandes collections.map(), il faut convertir l'itérateur en liste ou en tuple.map() peut rendre votre code plus compact, mais il faut veiller à ne pas trop compliquer la compréhension si la fonction appliquée devient trop complexe. Dans ce cas, une boucle for explicite pourrait être préférable.La fonction intégrée filter() permet de filtrer un itérable en ne retenant que les éléments qui satisfont une condition. Concrètement, elle prend en argument :
None comme fonction, Python utilise implicitement l'identité (c'est-à-dire qu'il ne garde que les éléments "vrais", en excluant par exemple 0, False, None, ou des chaînes vides).
filter(function, iterable)
filter, qui est un itérateur. Pour obtenir une liste ou un tuple, il faudra le convertir explicitement avec list() ou tuple().
Exemple avec une liste, filtrer les nombres pairs:
nombres = [1, 2, 3, 4, 5, 6, 7, 8]
# Utilisation d'une fonction lambda pour ne garder que les nombres pairs
nombres_pairs = filter(lambda x: x % 2 == 0, nombres)
# Conversion en liste pour afficher le résultat
print(list(nombres_pairs)) # Affiche : [2, 4, 6, 8]
Explication : La lambda retourne True pour les nombres divisibles par 2 (pairs). Seuls ces nombres sont conservés dans l'itérateur.
Bien que map() et filter() soient toutes deux des fonctions d'ordre supérieur utilisées pour traiter des itérables, elles remplissent des rôles différents :
True.# map exemple
# Calculer le carré de chaque nombre
carres = map(lambda x: x ** 2, [1, 2, 3, 4])
print(list(carres)) # Affiche : [1, 4, 9, 16]
# filter exemple
# Ne garder que les nombres pairs
pairs = filter(lambda x: x % 2 == 0, [1, 2, 3, 4])
print(list(pairs)) # Affiche : [2, 4]
En résumé :
map() transforme chaque élément (même si le résultat peut être identique à l'élément d'origine);filter() sélectionne uniquement les éléments répondant à une condition donnée, sans les transformer.La fonction reduce() de la bibliothèque functools est un outil de programmation fonctionnelle qui permet de réduire un itérable à une seule valeur en appliquant de manière cumulative une fonction binaire à ses éléments.
from functools import reduce
Le but: Transformer une séquence (liste, tuple, etc.) en une unique valeur (par exemple, la somme, le produit, la concaténation, etc.).
Via une fonction binaire: Une fonction qui prend deux arguments. La fonction est d'abord appliquée aux deux premiers éléments de l'itérable, puis le résultat est combiné avec le troisième élément, et ainsi de suite jusqu'à obtenir une valeur finale.
Syntaxe:
from functools import reduce
reduce(function, iterable[, initializer])
# exemple Calcul de la somme d'une liste de nombres
nombres = [1, 2, 3, 4]
somme = 0
for n in nombres:
somme += n
print(somme) # Affiche : 10
# avec reduce()
from functools import reduce
nombres = [1, 2, 3, 4]
somme = reduce(lambda x, y: x + y, nombres)
print(somme) # Affiche : 10
Explication:
La lambda reçoit deux nombres à chaque appel :
Autre exemple, Calcul du produit des éléments d'une liste:
from functools import reduce
nombres = [2, 3, 4]
produit = reduce(lambda x, y: x * y, nombres)
print(produit) # Affiche : 24
Explication:
La lambda reçoit deux nombres à chaque appel :
Avec l'utilisation d'un initializer:
from functools import reduce
nombres = [1, 2, 3, 4]
# On commence la somme avec 10
somme = reduce(lambda x, y: x + y, nombres, 10)
print(somme) # Affiche : 20 (10 + 1 + 2 + 3 + 4)
Quand utiliser reduce() ?
reduce() peut parfois être moins lisible pour ceux qui ne sont pas familiers avec la programmation fonctionnelle.sum() pour la somme ou math.prod() pour le produit en Python 3.8+) peuvent être plus simples et clairs.
La fonction reduce() est un outil puissant pour réduire un itérable à une seule valeur en appliquant une fonction binaire de manière cumulative. Elle fait partie de la programmation fonctionnelle en Python et se trouve dans le module functools.
Que ce soit pour calculer une somme, un produit ou pour combiner des chaînes, reduce() offre une syntaxe concise qui peut simplifier votre code lorsque l'opération se prête bien à une réduction.
La fonction intégrée sorted() permet de trier un itérable et retourne toujours une nouvelle liste triée sans modifier l’itérable d’origine. Elle accepte tout type d’itérable : listes, tuples, chaînes de caractères, dictionnaires, ensembles, etc.
Syntaxe générale:
sorted(iterable, key=None, reverse=False)
True, le tri se fait dans l’ordre décroissant.# exemple Tri d'une liste en ordre décroissant
nombres = [3, 1, 4, 2]
resultat = sorted(nombres, reverse=True)
print(resultat) # Affiche : [4, 3, 2, 1]
Points à retenir:
sorted() retourne une nouvelle liste triée et ne modifie pas l'itérable original.key qui permet d"appliquer une fonction de transformation sur chaque élément avant comparaison.reverse=True permet de trier dans l'ordre décroissant.sorted() fonctionne avec tout itérable (listes, tuples, chaînes, dictionnaires, ensembles…).La fonction sorted() est très puissante et flexible, vous permettant de trier des données provenant de différents types d'itérables tout en offrant la possibilité de personnaliser le tri via l'argument key et d'inverser l'ordre avec reverse. Elle favorise un code clair et non destructif puisque l'itérable d'origine reste inchangé.
Trois mots clés à connaître:
break sert à interrompre une boucle immédiatementcontinue sert à sauter une itération, ("passer" un tour de boucle)pass est un simple "placeholder", pratique quand on écrit une signature de fonction ou méthode, mais que cette dernière n'est pas encore implémentéeComment écrire une fonction? Via le mot clé def:
# exemple de fonction
def bonjour():
print("Hello, monde !")
return "Coucou"
# Appel de la fonction
bonjour() # affiche Hello, monde !
On peut bien entendu définir une valeur par défaut pour un argument (def saluer(nom="Inconnu"):). Et utilisé les arguments nommés, ce qui permet une flexibilité au niveau de l'ordre:
# exemple d'utilisation des arguments nommés
def infos(nom, age):
print(f"Nom : {nom}, Âge : {age}")
infos(age=25, nom="Alice")
Une fonction lambda en Python est une fonction anonyme, c'est-à-dire une fonction sans nom, définie en une seule expression à l'aide du mot-clé lambda. Elle permet de créer rapidement des fonctions simples qui retournent le résultat d'une expression.
Syntaxe: lambda param1, param2, ..., paramN: expression
: peut être un calcul, une opération, une transformation ou une condition.Exemple avec un expression représentant une opération simple:
# Addition
addition = lambda x, y: x + y
print(addition(4, 5)) # ➝ 9
# Multiplier par 10
fois_dix = lambda x: x * 10
print(fois_dix(3)) # ➝ 30
# Mettre une chaîne en majuscules
majuscule = lambda s: s.upper()
print(majuscule("hello")) # ➝ "HELLO"
Exemple avec un expression représentant une condition (avec opérateur ternaire x if condition else y):
# Vérifier si un nombre est pair ou impair
pair_ou_impair = lambda x: "Pair" if x % 2 == 0 else "Impair"
print(pair_ou_impair(7)) # ➝ "Impair"
# Trouver le plus grand de deux nombres
maximum = lambda a, b: a if a > b else b
print(maximum(10, 5)) # ➝ 10
# Retourner un message en fonction de l'âge
age_message = lambda age: "Adulte" if age >= 18 else "Mineur"
print(age_message(16)) # ➝ "Mineur"
Une lambda est souvent utilisée dans des fonctions comme map(), filter(), sorted(), etc.
En Python, une fonction variadique est une fonction qui accepte un nombre variable d'arguments positionnels. Au lieu de déclarer explicitement un nombre fixe de paramètres, vous pouvez définir un paramètre spécial préfixé par l'astérisque (*). Ce paramètre capte tous les arguments supplémentaires passés lors de l'appel de la fonction et les regroupe dans un tuple.
L'utilisation de *args présente plusieurs avantages :
*argsPour définir une fonction qui accepte un nombre variable d'arguments positionnels, on utilise la syntaxe suivante :
# Syntaxe de base
def ma_fonction(*args):
print(args) # args est un tuple contenant tous les arguments supplémentaires
Lorsque vous appelez cette fonction, tous les arguments passés en plus des paramètres positionnels obligatoires (s'il y en a) sont collectés dans le tuple args.
# Exemple simple
def afficher_elements(*args):
for arg in args:
print(arg)
afficher_elements(1, 2, 3) # Affiche 1, 2, 3
Explication du fonctionnement:
Lorsque vous appelez une fonction variadique, Python fait la répartition suivante :
*.# Exemple avec paramètres fixes et args :
def additionner(a, b, *args):
total = a + b
for nombre in args:
total += nombre
return total
print(additionner(10, 20)) # Pas d'arguments supplémentaires → 30
print(additionner(10, 20, 30, 40)) # 10+20+30+40 = 100
*args pour déballer des séquencesL"opérateur * sert également lors des appels de fonction pour "déballer" une séquence (comme une liste ou un tuple) en arguments positionnels.
def point(x, y):
return f"({x}, {y})"
coordonnees = (3, 7)
# Sans déballage, Python pense que l'on passe un seul argument.
# point(coordonnees) --> Erreur !
# Avec déballage :
print(point(*coordonnees)) # Affecte 3 à x et 7 à y → affiche (3, 7)
Ainsi, l'astérisque permet de convertir un tuple ou une liste en plusieurs arguments positionnels pour la fonction.
paramètres normaux, *args et **kwargsEn plus de *args pour les arguments positionnels, Python offre également **kwargs pour capturer des arguments nommés supplémentaires dans un dictionnaire. Même si le sujet porte sur *args, il est intéressant de savoir qu'ils peuvent être utilisés ensemble.
def fonction_mixte(a, b, *args, **kwargs):
print(f"a = {a}, b = {b}")
print("args =", args)
print("kwargs =", kwargs)
fonction_mixte(1, 2, 3, 4, 5, nom="Alice", age=30)
Affiche:
a = 1, b = 2
args = (3, 4, 5)
kwargs = {'nom': 'Alice', 'age': 30}
Ordre des paramètres
La déclaration des paramètres d'une fonction doit respecter l'ordre suivant :
# exemple
def exemple(a, b=10, *args, c, **kwargs):
# ici, c est un paramètre "mot-clé uniquement"
print(a, b, args, c, kwargs)
# Appel correct :
exemple(1, 2, 3, 4, c=5, d=6)
Itération et accès aux éléments
Puisque args est un tuple, vous pouvez :
for.args[0], args[1], …).Le paramètre *args peut être vide s'il n'y a pas d'arguments supplémentaires. Cela permet donc d'appeler la fonction sans se soucier d'erreurs liées au nombre d'arguments.
def afficher_message(*args):
if args:
print("Messages :", args)
else:
print("Aucun message reçu.")
afficher_message() # Affiche "Aucun message reçu."
afficher_message("Bonjour", "Hi") # Affiche le tuple de messages
* dans l'appel de fonction.# Exemple d'une fonction d'addition
def additionner(*nombres):
total = sum(nombres)
return total
print(additionner(5, 10)) # Affiche 15
print(additionner(1, 2, 3, 4, 5)) # Affiche 15
# Exemple d'une fonction qui concatène des chaînes
def concatener(*mots):
# Les arguments sont des chaînes de caractères
return " ".join(mots)
resultat = concatener("Ceci", "est", "un", "exemple")
print(resultat) # Affiche "Ceci est un exemple"
# Exemple avec paramètres positionnels obligatoires
def rapport(nom, *points):
print(f"Rapport pour {nom}:")
for point in points:
print("-", point)
rapport("Alice", "Observation 1", "Observation 2", "Observation 3")
# sortie
Rapport pour Alice:
- Observation 1
- Observation 2
- Observation 3
L'utilisation du paramètre *args en Python est une technique puissante pour créer des fonctions pouvant accepter un nombre variable d'arguments. Elle repose sur le concept de déballage des arguments dans un tuple, ce qui permet d'itérer sur ceux-ci et de les utiliser de manière flexible. Ce mécanisme, complété par l'usage de **kwargs pour les arguments nommés, contribue à écrire un code Python plus générique et adaptable à différentes situations.
En résumé :
*args permet de collecter tous les arguments positionnels supplémentaires dans un tuple.*.En programmation fonctionnelle, la partial application consiste à fixer partiellement certains arguments d'une fonction pour créer une nouvelle fonction ayant une signature réduite. En Python, le module functools fournit la fonction partial qui permet de "prédéfinir" certains paramètres d'une fonction de sorte que, lors de l'appel ultérieur, vous n'ayez plus qu'à fournir les arguments restants.
Cela offre plusieurs avantages :
functools.partialLa signature est la suivante : functools.partial(func, /, *args, **kwargs)
func: La fonction de base à laquelle vous souhaitez fixer certains arguments.*args: Les arguments positionnels à fixer (les « préfixes » des arguments).**kwargs: Les arguments nommés à fixer.Lorsque la fonction partielle est appelée, les arguments fournis lors de l'appel sont ajoutés (concaténés) aux arguments déjà fixés. Ainsi, l'appel effectif est équivalent à:
func(*args, *new_args, **{**kwargs, **new_kwargs})
Les objets partiels créés possèdent trois attributs en lecture seule :
func: la fonction originale.args: les arguments positionnels pré-fixés.keywords: le dictionnaire des arguments nommés pré-fixés.Supposons que nous ayons une fonction multiply qui multiplie deux nombres. Nous allons créer une fonction partielle qui fixe le premier facteur à 2 pour obtenir une fonction qui double un nombre.
from functools import partial
def multiply(x, y):
return x * y
# Crée une fonction partielle qui fixe x à 2
double = partial(multiply, 2)
print(double(4)) # Affiche 8
Ici, l'appel double(4) revient à appeler multiply(2, 4).
Supposons que nous ayons une fonction multiply qui multiplie deux nombres. Nous allons créer une fonction partielle qui fixe le premier facteur à 2 pour obtenir une fonction qui double un nombre.
from functools import partial
def multiply(x, y):
return x * y
# Crée une fonction partielle qui fixe x à 2
double = partial(multiply, 2)
print(double(4)) # Affiche 8
Ici, l'appel double(4) revient à appeler multiply(2, 4).
Considérons une fonction avec plusieurs paramètres et des arguments nommés :
from functools import partial
def func(u, v, w, x):
return u * 4 + v * 3 + w * 2 + x
# Création d'une fonction partielle fixant les trois premiers paramètres
p = partial(func, 5, 6, 7)
print(p(8)) # Calcule 5*4 + 6*3 + 7*2 + 8 = 60
Vous voyez ici que les arguments 5, 6, 7 sont « enrobés » dans la fonction partielle. L'appel de p(8) fournit la valeur pour le dernier paramètre.
Nous pouvons examiner les attributs de l'objet partiel:
from functools import partial
def add(a, b):
return a + b
add_five = partial(add, 5)
print(add_five.func) # Affiche la fonction 'add'
print(add_five.args) # Affiche (5,)
print(add_five.keywords) # Affiche {} (vide dans ce cas)
Ces attributs permettent de connaître quels arguments ont été "baked" lors de la création de l'objet partiel.
Une chose importante à noter est que les arguments passés à partial sont « baked in » au moment de la création. Si vous passez un objet mutable, la référence est conservée:
from functools import partial
def multiply(item, multiplier):
return item * multiplier
liste = ["a"]
p_multiply = partial(multiply, liste, 2)
print(p_multiply()) # ['a', 'a']
# Même si on réaffecte la variable 'liste', le partial conserve la référence initiale :
liste = ["a", "b"]
print(p_multiply()) # Toujours ['a', 'a']
# En revanche, si on modifie l'objet mutable, le changement sera reflété
liste = ["a"]
p_multiply = partial(multiply, liste, 2)
print(p_multiply()) # ['a', 'a']
liste.append("b")
print(p_multiply()) # ['a', 'b', 'a', 'b']
Cela montre que, pour un objet mutable, on ne prend pas une copie, mais la référence (source: devcuriosity.com - python-partial-function).
partial avec des méthodes - le cas de partialmethodLorsque vous souhaitez fixer des arguments pour des méthodes de classe, il faut prendre en compte que la première variable (habituellement self) est automatiquement passée. Un simple usage de partial ne fonctionnera pas comme attendu. Pour cela, Python offre partialmethod.
from functools import partialmethod
class Article:
def set_platform(self, platform):
self.platform = platform
# Création d'une méthode partielle qui fixe platform à "substack"
set_substack = partialmethod(set_platform, "substack")
article = Article()
article.set_substack()
print(article.platform) # Affiche 'substack'
Ici, partialmethod gère correctement le passage du self et fixe l'argument platform.
Les fonctions partielles sont particulièrement utiles dans les situations suivantes.
Par exemple, pour des interfaces graphiques (Tkinter, PyQt) ou d'autres frameworks où vous devez fournir une fonction à appeler lors d'un événement et que certains paramètres restent constants.
Exemple : fixer la valeur d'un paramètre lors de l'appel d'une fonction de gestion d'événement avec partial.
Lorsque vous avez une fonction générique à laquelle vous appliquez fréquemment certains arguments constants (par exemple, la conversion d'une chaîne en entier avec une base fixe, voir l'exemple avec int et base=2).
from functools import partial
from math import prod # ou une fonction multiply personnalisée
# Créer une fonction qui élève toujours un nombre au carré
square = partial(lambda x, exponent: x ** exponent, exponent=2)
Dans des cas où le résultat d'une fonction doit être transformé avant d'être passé à une autre, la fonction partielle peut fixer des paramètres dans des fonctions intermédiaires.
Les fonctions partielles représentent un puissant outil de programmation fonctionnelle en Python. Grâce à functools.partial et partialmethod vous pouvez "figer" une partie des arguments d'une fonction ou méthode pour obtenir de nouvelles fonctions plus spécifiques et simples à utiliser. Cela conduit à du code plus lisible et à la réduction de la répétition de valeurs constantes.
En Python, tout est accessible. Il n'existe pas de mot-clé comme private ou protected que l'on retrouve dans d'autres langages (par exemple, Php ou C++).
Les développeurs se fient aux conventions de nommage pour signaler les intentions d'usage du code.
Placer un underscore (_) devant le nom d'une fonction ou d'une variable indique qu'elle est destinée à être « privée » ou pour un usage interne uniquement.
Cette convention est largement reconnue et respectée par la communauté, mais elle n'empêche pas l'accès direct à la fonction depuis l'extérieur.
Dans une classe, on utilisera le __ (double underscore), on parle de name mangling. La POO n'est pas détaillée ici.
Dans un module, en plus de _, on peut utiliser __all__ pour ne pas exporter certaines fonctions
Il est important de bien documenté son code. Voici, un exemple en Python:
def addition(a, b):
"""
Cette fonction retourne la somme de deux nombres.
Paramètres:
a (int, float) : Premier nombre
b (int, float) : Deuxième nombre
Retourne:
int, float : La somme de a et b
"""
return a + b
Remarque: la documentation est ainsi accessible via: print(addition.__doc__) ou help(addition).