37. Fonctions intégrées et outils utiles
Python fournit une riche collection de fonctions intégrées qui sont toujours disponibles sans avoir besoin d’importer des modules. Ces fonctions constituent la base de la programmation Python au quotidien, en vous aidant à travailler efficacement avec les données, les séquences et les collections. Dans ce chapitre, nous allons explorer les outils intégrés les plus utiles de Python et apprendre à les exploiter pour écrire du code plus propre et plus expressif.
Comprendre le système de types de Python
Avant d’examiner des fonctions intégrées spécifiques, il est utile de comprendre comment Python organise ses types de données. Cette connaissance vous aidera à prévoir quelles opérations fonctionnent avec quels types et à comprendre les messages d’erreur lorsqu’ils surviennent.
Les types de données de Python peuvent être compris selon deux perspectives complémentaires :
Hiérarchie des types : comment les types sont liés
Cela montre comment Python organise les types en familles en fonction de ce qu’ils SONT.
Vue basée sur les capacités : ce que les types peuvent faire
Pour les fonctions intégrées, ce qui compte davantage, c’est ce que les types peuvent FAIRE :
Capacités clés :
- Itérable(iterable) : Peut être utilisé dans des boucles
for→ Fonctionne avecsum(),any(),all(),sorted() - Collection(collection) : Itérable avec
len()→ Fonctionne aveclen()et l’opérateurin - Séquence(sequence) : Collection avec indexation → Prend en charge
[index]et le slicing[start:end]
Pourquoi c’est important
Les fonctions intégrées nécessitent des capacités spécifiques :
| Function | Requires | Works With |
|---|---|---|
len() | Collection | str, list, dict, set, tuple |
sum() | Iterable of numbers | list, tuple, set, range, generator |
sorted() | Iterable | str, list, dict, set, tuple |
[index] | Sequence | str, list, tuple, range |
Comprendre ces catégories vous aide à :
- Prévoir quelles fonctions marchent avec quels types
- Comprendre des messages d’erreur comme « object is not iterable »
- Savoir quand vous pouvez indexer (
[0]) et quand vous ne pouvez que boucler (for)
37.1) Fonctions intégrées courantes (len, sum, min, max, abs, round)
Les fonctions intégrées les plus fréquemment utilisées de Python vous aident à effectuer des opérations courantes sur les données sans écrire de boucles ou de logique complexe. Ces fonctions sont optimisées, lisibles et constituent la base d’un code pythonique.
37.1.1) Mesurer une longueur avec len()
La fonction len() renvoie le nombre d’éléments dans une collection. Elle fonctionne avec les chaînes de caractères, les listes, les tuples, les dictionnaires, les ensembles, et tout autre type de collection.
# Compter les caractères dans une chaîne
message = "Hello, World!"
print(len(message)) # Output: 13
# Compter les éléments dans une liste
scores = [85, 92, 78, 90, 88]
print(len(scores)) # Output: 5
# Compter les paires clé-valeur dans un dictionnaire
student = {"name": "Bob", "age": 21, "major": "CS"}
print(len(student)) # Output: 3
# Compter les éléments uniques dans un ensemble
unique_ids = {101, 102, 103, 101, 102} # Doublons supprimés
print(len(unique_ids)) # Output: 3La fonction len() est particulièrement utile lorsque vous devez connaître la taille des données avant de les traiter :
# Traiter des données en fonction de leur taille
data = [12, 45, 23, 67, 89, 34]
if len(data) < 5:
print("Not enough data for analysis")
else:
print(f"Analyzing {len(data)} data points") # Output: Analyzing 6 data points
average = sum(data) / len(data)
print(f"Average: {average}") # Output: Average: 45.037.1.2) Calculer des totaux avec sum()
La fonction sum() additionne tous les nombres d’un itérable. C’est bien plus propre que d’écrire une boucle pour accumuler des valeurs.
# Additionner une liste de nombres
prices = [19.99, 24.50, 15.75, 32.00]
total = sum(prices)
print(f"Total: ${total}") # Output: Total: $92.24
# Additionner un tuple
daily_steps = (8500, 10200, 7800, 9500, 11000)
weekly_total = sum(daily_steps)
print(f"Total steps this week: {weekly_total}") # Output: Total steps this week: 47000
# Additionner un range
total_1_to_100 = sum(range(1, 101))
print(total_1_to_100) # Output: 5050Un exemple pratique combinant sum() et len() pour calculer des moyennes :
# Calculer la moyenne d’un score de test
test_scores = [88, 92, 79, 85, 90, 87]
total_score = sum(test_scores)
num_tests = len(test_scores)
average_score = total_score / num_tests
print(f"Average score: {average_score:.1f}") # Output: Average score: 86.8Limitation importante : sum() ne fonctionne qu’avec des nombres. Vous ne pouvez pas l’utiliser pour concaténer des chaînes ou combiner des listes :
# Ceci déclenche TypeError
words = ["Hello", " ", "World"]
# sentence = sum(words) # TypeError: unsupported operand type(s)37.1.3) Trouver des extrêmes avec min() et max()
Les fonctions min() et max() trouvent les plus petites et plus grandes valeurs dans un itérable. Elles fonctionnent avec les nombres, les chaînes et tout objet pouvant être comparé.
# Trouver le minimum et le maximum de nombres
temperatures = [72, 68, 75, 70, 73, 69]
coldest = min(temperatures)
warmest = max(temperatures)
print(f"Temperature range: {coldest}°F to {warmest}°F")
# Output: Temperature range: 68°F to 75°F
# Trouver le minimum et le maximum de chaînes (alphabétiquement)
names = ["Zoe", "Alice", "Bob", "Charlie"]
first_alphabetically = min(names)
last_alphabetically = max(names)
print(f"First: {first_alphabetically}, Last: {last_alphabetically}")
# Output: First: Alice, Last: ZoeVous pouvez aussi passer plusieurs arguments directement au lieu d’une collection :
# Comparer des valeurs individuelles
lowest = min(45, 23, 67, 12, 89)
highest = max(45, 23, 67, 12, 89)
print(f"Lowest: {lowest}, Highest: {highest}")
# Output: Lowest: 12, Highest: 89
# Utile pour comparer quelques valeurs spécifiques
price1 = 19.99
price2 = 24.50
price3 = 15.75
cheapest = min(price1, price2, price3)
print(f"Cheapest option: ${cheapest}") # Output: Cheapest option: $15.7537.1.4) Obtenir des valeurs absolues avec abs()
La fonction abs() renvoie la valeur absolue d’un nombre — sa distance à zéro, toujours positive. C’est utile lorsque vous vous intéressez à la magnitude mais pas à la direction.
# Valeur absolue de nombres négatifs
print(abs(-42)) # Output: 42
print(abs(-3.14)) # Output: 3.14
# Valeur absolue de nombres positifs (inchangée)
print(abs(42)) # Output: 42
print(abs(3.14)) # Output: 3.14
# Valeur absolue de zéro
print(abs(0)) # Output: 0Un cas d’usage courant consiste à calculer des différences où la direction n’a pas d’importance :
# Calculer un changement de température (magnitude uniquement)
morning_temp = 65
evening_temp = 72
temperature_change = abs(evening_temp - morning_temp)
print(f"Temperature changed by {temperature_change}°F")
# Output: Temperature changed by 7°F37.1.5) Arrondir des nombres avec round()
La fonction round() arrondit un nombre à un nombre spécifié de décimales. Sans second argument, elle arrondit à l’entier le plus proche.
# Arrondir à l’entier le plus proche
print(round(3.7)) # Output: 4
print(round(3.2)) # Output: 3
print(round(3.5)) # Output: 4
print(round(4.5)) # Output: 4 (arrondit vers le nombre pair le plus proche)
# Arrondir à un nombre précis de décimales
price = 19.876
print(round(price, 2)) # Output: 19.88 (2 decimal places)
print(round(price, 1)) # Output: 19.9 (1 decimal place)
# Arrondir à des décimales négatives (arrondit aux dizaines, centaines, etc.)
population = 1234567
print(round(population, -3)) # Output: 1235000 (nearest thousand)
print(round(population, -4)) # Output: 1230000 (nearest ten thousand)Remarque sur les valeurs à mi-chemin : Lorsqu’on arrondit un nombre qui se trouve exactement à mi-chemin entre deux entiers, Python applique une règle spéciale. Par exemple, 2.5 est exactement à mi-chemin entre 2 et 3. Vous pourriez vous attendre à ce qu’il arrondisse vers le haut à 3, mais Python arrondit vers le voisin qui est un nombre pair — dans ce cas, 2.
Cela s’appelle « banker's rounding » ou « round half to even ». Cela fait partie du standard IEEE 754 et aide à réduire le biais sur de nombreuses opérations d’arrondi.
# Les valeurs à mi-chemin sont arrondies vers le nombre pair le plus proche
print(round(0.5)) # Output: 0 (0 is even)
print(round(1.5)) # Output: 2 (2 is even)
print(round(2.5)) # Output: 2 (2 is even)
print(round(3.5)) # Output: 4 (4 is even)
print(round(4.5)) # Output: 4 (4 is even)37.2) Énumérer des séquences avec enumerate()
Lorsque vous bouclez sur une séquence, vous avez souvent besoin à la fois de l’élément et de sa position. La fonction enumerate() fournit les deux, éliminant le besoin de variables de compteur manuelles.
37.2.1) Le problème des compteurs manuels
Avant d’apprendre enumerate(), les programmeurs utilisent souvent une variable compteur pour suivre la position :
# Approche avec compteur manuel (fonctionne mais pas idéale)
fruits = ["apple", "banana", "cherry", "date"]
index = 0
for fruit in fruits:
print(f"{index}: {fruit}")
index += 1
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateCette approche a plusieurs inconvénients :
- Variable supplémentaire à gérer (
index) - Facile d’oublier d’incrémenter le compteur
37.2.2) Utiliser enumerate() pour la position et la valeur
La fonction enumerate() résout ce problème élégamment. Elle prend un itérable et renvoie des paires (index, élément) :
# Utiliser enumerate() - plus propre et plus pythonique
fruits = ["apple", "banana", "cherry", "date"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateLa syntaxe for index, fruit in enumerate(fruits) utilise le dépaquetage de tuple (comme nous l’avons appris au Chapitre 15). À chaque itération, enumerate() fournit un tuple comme (0, "apple"), qui est dépaqueté dans les variables index et fruit.
Voici ce que enumerate() produit réellement :
# Voir directement la sortie de enumerate
fruits = ["apple", "banana", "cherry"]
enumerated = list(enumerate(fruits))
print(enumerated)
# Output: [(0, 'apple'), (1, 'banana'), (2, 'cherry')]37.2.3) Démarrer l’énumération à partir d’un nombre différent
Par défaut, enumerate() commence à compter à partir de 0. Vous pouvez spécifier un nombre de départ différent avec le paramètre start :
# Commencer à compter à partir de 1 (utile pour l’affichage)
tasks = ["Write code", "Test code", "Deploy code"]
for number, task in enumerate(tasks, start=1):
print(f"Step {number}: {task}")
# Output:
# Step 1: Write code
# Step 2: Test code
# Step 3: Deploy codeC’est particulièrement utile lorsque vous affichez des listes numérotées aux utilisateurs, qui s’attendent généralement à ce que le comptage commence à 1 :
# Menu avec options numérotées
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
print(f"{number}. {item}")
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit37.2.4) enumerate() avec des chaînes et d’autres itérables
La fonction enumerate() fonctionne avec n’importe quel itérable, pas seulement les listes :
# Énumérer les caractères d’une chaîne
word = "Python"
for position, letter in enumerate(word):
print(f"Letter {position}: {letter}")
# Output:
# Letter 0: P
# Letter 1: y
# Letter 2: t
# Letter 3: h
# Letter 4: o
# Letter 5: n
# Énumérer un tuple
coordinates = (10, 20, 30, 40)
for index, value in enumerate(coordinates):
print(f"Coordinate {index}: {value}")
# Output:
# Coordinate 0: 10
# Coordinate 1: 20
# Coordinate 2: 30
# Coordinate 3: 40La fonction enumerate() rend le code plus lisible et moins sujet aux erreurs. Chaque fois que vous avez besoin à la fois de la position et de la valeur dans une boucle, utilisez enumerate() plutôt que de gérer un compteur manuellement.
37.3) Combiner des séquences avec zip()
La fonction zip() combine plusieurs itérables élément par élément, en créant des paires (ou des tuples) d’éléments correspondants. C’est extrêmement utile lorsque vous devez traiter simultanément des données liées provenant de séquences séparées.
37.3.1) Comprendre comment zip() fonctionne
La fonction zip() prend deux itérables ou plus et renvoie un itérateur de tuples, où chaque tuple contient un élément de chaque itérable d’entrée :
# Combiner deux listes
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
combined = list(zip(names, ages))
print(combined)
# Output: [('Alice', 25), ('Bob', 30), ('Charlie', 35)]Le nom « zip » vient de la fermeture éclair sur les vêtements — elle combine deux côtés séparés en une structure jointe, élément par élément.
Voici une représentation visuelle de la façon dont zip() associe les éléments :
37.3.2) Utiliser zip() dans des boucles
L’usage le plus courant de zip() est dans les boucles for, lorsque vous devez itérer sur plusieurs séquences simultanément :
# Traiter des données parallèles
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for student, score in zip(students, scores):
print(f"{student} scored {score}")
# Output:
# Alice scored 92
# Bob scored 85
# Charlie scored 88
# Diana scored 95C’est beaucoup plus propre que d’utiliser des indices :
# Sans zip() - plus complexe et plus sujet aux erreurs
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for i in range(len(students)):
print(f"{students[i]} scored {scores[i]}")
# Same output, but more code and potential for index errors37.3.3) Gérer des séquences de longueurs différentes
Lorsque les séquences d’entrée ont des longueurs différentes, zip() s’arrête lorsque la séquence la plus courte est épuisée :
# Séquences de longueur inégale
names = ["Alice", "Bob", "Charlie", "Diana"]
ages = [25, 30] # Seulement 2 âges
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Output:
# Alice is 25 years old
# Bob is 30 years old
# (Charlie and Diana are not processed)Ce comportement évite les erreurs, mais peut entraîner une perte silencieuse de données si vous n’y prenez pas garde. Vérifiez toujours que vos séquences ont les longueurs attendues :
# Vérifier les différences de longueur
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30]
if len(names) != len(ages):
print(f"Warning: {len(names)} names but {len(ages)} ages")
print("Only processing the first", min(len(names), len(ages)), "entries")
# Output: Warning: 3 names but 2 ages
# Output: Only processing the first 2 entries
# Continuer avec zip() - il s’arrêtera à la plus courte
for name, age in zip(names, ages):
print(f"{name} is {age} years old")37.3.4) Zipper plus de deux séquences
La fonction zip() peut combiner n’importe quel nombre d’itérables :
# Combiner trois séquences
products = ["Laptop", "Mouse", "Keyboard"]
prices = [999.99, 24.99, 79.99]
quantities = [5, 20, 15]
print("Inventory Report:")
for product, price, quantity in zip(products, prices, quantities):
total_value = price * quantity
print(f"{product}: ${price} × {quantity} = ${total_value:.2f}")
# Output:
# Inventory Report:
# Laptop: $999.99 × 5 = $4999.95
# Mouse: $24.99 × 20 = $499.80
# Keyboard: $79.99 × 15 = $1199.8537.3.5) Créer des dictionnaires avec zip()
Un motif puissant consiste à utiliser zip() pour créer des dictionnaires à partir de séquences de clés et de valeurs séparées :
# Créer un dictionnaire à partir de deux listes
keys = ["name", "age", "city"]
values = ["Alice", 25, "Boston"]
person = dict(zip(keys, values))
print(person)
# Output: {'name': 'Alice', 'age': 25, 'city': 'Boston'}37.4) Agrégation booléenne avec any() et all()
Les fonctions any() et all() testent des conditions sur des itérables entiers, en renvoyant un seul résultat booléen. Ce sont des outils puissants pour la validation et la prise de décision à partir de plusieurs conditions.
37.4.1) Comprendre any() : True si au moins un élément est True
La fonction any() renvoie True si au moins un élément dans un itérable est truthy (évalué à True). Si tous les éléments sont falsy, elle renvoie False :
# Exemples de base avec any()
print(any([True, False, False])) # Output: True (at least one True)
print(any([False, False, False])) # Output: False (all False)
print(any([False, True, True])) # Output: True (multiple True values)
# Itérables vides
print(any([])) # Output: False (no elements to be True)La fonction any() utilise les règles de truthiness de Python (comme nous l’avons appris au Chapitre 7). Les nombres non nuls, les chaînes non vides et les collections non vides sont truthy :
# any() avec différentes valeurs truthy/falsy
print(any([0, 0, 1])) # Output: True (1 is truthy)
print(any([0, 0, 0])) # Output: False (all zeros are falsy)
print(any(["", "", "text"])) # Output: True ("text" is truthy)
print(any(["", "", ""])) # Output: False (empty strings are falsy)37.4.2) Utilisations pratiques de any()
Exemple : vérifier si une condition est satisfaite
# Vérifier si un score est insuffisant (inférieur à 60)
scores = [75, 82, 55, 90, 88]
has_failing_grade = any(score < 60 for score in scores)
if has_failing_grade:
print("Warning: At least one failing grade")
# Output: Warning: At least one failing grade
else:
print("All grades are passing")37.4.3) Comprendre all() : True seulement si tous les éléments sont True
La fonction all() renvoie True uniquement si tous les éléments d’un itérable sont truthy. Si un seul élément est falsy, elle renvoie False :
# Exemples de base avec all()
print(all([True, True, True])) # Output: True (all True)
print(all([True, False, True])) # Output: False (one False)
print(all([True, True, False])) # Output: False (one False)
# Itérables vides
print(all([])) # Output: True (vacuous truth - no False elements)Le comportement avec des itérables vides peut sembler surprenant : all([]) renvoie True. Cela s’appelle la vérité vacante — l’énoncé « tous les éléments sont True » est techniquement vrai lorsqu’il n’y a aucun élément pour le contredire.
# all() avec différentes valeurs truthy/falsy
print(all([1, 2, 3])) # Output: True (all non-zero)
print(all([1, 0, 3])) # Output: False (0 is falsy)
print(all(["a", "b", "c"])) # Output: True (all non-empty)
print(all(["a", "", "c"])) # Output: False (empty string is falsy)37.4.4) Utilisations pratiques de all()
Exemple : valider que toutes les conditions sont remplies
# Vérifier si tous les scores sont suffisants (60 ou plus)
scores = [75, 82, 68, 90, 88]
all_passing = all(score >= 60 for score in scores)
if all_passing:
print("Congratulations! All grades are passing")
# Output: Congratulations! All grades are passing
else:
print("Some grades need improvement")37.4.5) Évaluation en court-circuit dans any() et all()
any() et all() utilisent tous deux l’évaluation en court-circuit (comme nous l’avons appris au Chapitre 9). Ils arrêtent de vérifier dès que le résultat est déterminé :
# Fonction qui affiche lors de l’appel (pour montrer l’exécution)
def is_positive(n):
print(f"Checking {n}")
return n > 0
# any() s’arrête au premier True
print("Testing any():")
numbers = [0, 0, 1, 2, 3]
result = any(is_positive(n) for n in numbers)
# Output:
# Testing any():
# Checking 0
# Checking 0
# Checking 1
# (Stops here - doesn't check 2 or 3)
print(f"Result: {result}") # Output: Result: True
print("\nTesting all():")
numbers = [1, 2, 0, 3, 4]
result = all(is_positive(n) for n in numbers)
# Output:
# Testing all():
# Checking 1
# Checking 2
# Checking 0
# (Stops here - doesn't check 3 or 4)
print(f"Result: {result}") # Output: Result: FalseCela rend any() et all() efficaces — ils ne perdent pas de temps à vérifier des éléments après que le résultat a été déterminé.
37.5) Trier avec sorted() et des clés personnalisées
La fonction sorted() crée une nouvelle liste triée à partir de n’importe quel itérable. Contrairement à la méthode .sort() (qui ne fonctionne que sur les listes et les modifie sur place), sorted() fonctionne avec tout itérable et renvoie toujours une nouvelle liste.
37.5.1) Tri de base avec sorted()
La fonction sorted() organise les éléments dans l’ordre croissant par défaut :
# Trier des nombres
numbers = [42, 17, 93, 8, 55]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # Output: [8, 17, 42, 55, 93]
# La liste d’origine est inchangée
print(numbers) # Output: [42, 17, 93, 8, 55]
# Trier des chaînes (alphabétiquement)
names = ["Charlie", "Alice", "Bob", "Diana"]
sorted_names = sorted(names)
print(sorted_names) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']La fonction sorted() fonctionne avec n’importe quel itérable, pas seulement les listes :
# Trier un tuple (renvoie une liste)
coordinates = (5, 2, 8, 1, 9)
sorted_coords = sorted(coordinates)
print(sorted_coords) # Output: [1, 2, 5, 8, 9]
# Trier une chaîne (renvoie une liste de caractères)
word = "python"
sorted_letters = sorted(word)
print(sorted_letters) # Output: ['h', 'n', 'o', 'p', 't', 'y']
# Trier un ensemble (renvoie une liste triée)
unique_numbers = {5, 8, 2, 1}
sorted_unique = sorted(unique_numbers)
print(sorted_unique) # Output: [1, 2, 5, 8]37.5.2) Tri inversé
Utilisez le paramètre reverse=True pour trier dans l’ordre décroissant :
# Ordre décroissant pour des nombres
scores = [85, 92, 78, 95, 88]
highest_first = sorted(scores, reverse=True)
print(highest_first) # Output: [95, 92, 88, 85, 78]
# Ordre décroissant pour des chaînes (inverse alphabétique)
names = ["Charlie", "Alice", "Bob", "Diana"]
reverse_alpha = sorted(names, reverse=True)
print(reverse_alpha) # Output: ['Diana', 'Charlie', 'Bob', 'Alice']37.5.3) Comprendre le paramètre key
Le paramètre key est ce qui rend sorted() vraiment puissant. Il transforme la manière dont Python compare les éléments pendant le tri.
Qu’est-ce que le paramètre key ?
Le paramètre key accepte une fonction(function). Python appelle cette fonction sur chaque élément pour en extraire une « clé de comparaison », puis trie en se basant sur ces clés plutôt que sur les éléments d’origine.
Comment cela fonctionne étape par étape :
- Python appelle la fonction key sur chaque élément
- Python collecte toutes les clés
- Python trie en comparant ces clés
- Python renvoie les éléments d’origine dans le nouvel ordre
# Exemple : trier par longueur
words = ["python", "is", "awesome"]
# Étape 1 : Python appelle len() sur chaque mot
# len("python") → 6
# len("is") → 2
# len("awesome") → 7
# Étape 2 : Python a ces clés : [6, 2, 7]
# Étape 3 : Python trie les clés : [2, 6, 7]
# Étape 4 : Python renvoie les mots dans cet ordre : ["is", "python", "awesome"]
result = sorted(words, key=len)
print(result) # Output: ['is', 'python', 'awesome']Visualiser la fonction key :
Que peut être une fonction key ?
La fonction key doit :
- Accepter un argument (l’élément trié)
- Renvoyer une valeur que Python peut comparer (nombres, chaînes, tuples, etc.)
# Les fonctions intégrées fonctionnent très bien
sorted(numbers, key=abs) # Trier par valeur absolue
sorted(words, key=len) # Trier par longueur
sorted(names, key=str.lower) # Trier sans tenir compte de la casse
# Vos propres fonctions
def first_letter(word):
return word[0]
sorted(words, key=first_letter) # Trier par première lettre
# Fonctions lambda (Chapitre 23)
sorted(words, key=lambda w: w[-1]) # Trier par dernière lettreImportant : la fonction key est appelée une fois par élément
# Montrer quand la fonction key est appelée
def show_key(word):
print(f"Getting key for: {word}")
return len(word)
words = ["cat", "elephant", "dog"]
result = sorted(words, key=show_key)
# Output:
# Getting key for: cat
# Getting key for: elephant
# Getting key for: dog
print(result) # Output: ['cat', 'dog', 'elephant']Important : la fonction key est appelée une fois par élément
Remarquez que show_key est appelée exactement une fois pour chaque mot, et non de façon répétée pendant les comparaisons. Python est efficace : il extrait toutes les clés d’abord, les met en cache, puis trie en utilisant les clés mises en cache.
Pensez à key comme à la réponse à : « Quel aspect dois-je comparer ? »
key=len→ « Comparer par longueur »key=abs→ « Comparer par valeur absolue »key=str.lower→ « Comparer comme si tout était en minuscules »key=lambda x: x[1]→ « Comparer par le deuxième élément »
Le paramètre key vous permet de trier selon n’importe quelle propriété de vos éléments, ce qui rend sorted() incroyablement polyvalent.
37.5.4) Trier avec des fonctions intégrées comme clés
Les fonctions intégrées de Python font d’excellentes fonctions key :
# Trier par valeur absolue
numbers = [-5, 2, -8, 1, -3, 7]
sorted_by_magnitude = sorted(numbers, key=abs)
print(sorted_by_magnitude) # Output: [1, 2, -3, -5, 7, -8]
# Trier des chaînes sans tenir compte de la casse
names = ["alice", "Bob", "CHARLIE", "diana"]
sorted_case_insensitive = sorted(names, key=str.lower)
print(sorted_case_insensitive) # Output: ['alice', 'Bob', 'CHARLIE', 'diana']37.5.5) Trier des structures de données complexes
Lors du tri de listes de tuples ou de listes, vous pouvez utiliser l’indexation pour indiquer par quel élément trier :
# Trier des tuples par le deuxième élément
students = [
("Alice", 92),
("Bob", 85),
("Charlie", 88),
("Diana", 95)
]
# Trier par score (deuxième élément)
by_score = sorted(students, key=lambda student: student[1])
print(by_score)
# Output: [('Bob', 85), ('Charlie', 88), ('Alice', 92), ('Diana', 95)]
# Trier par score décroissant
by_score_desc = sorted(students, key=lambda student: student[1], reverse=True)
print(by_score_desc)
# Output: [('Diana', 95), ('Alice', 92), ('Charlie', 88), ('Bob', 85)]Remarque : Nous utilisons lambda ici (comme nous l’avons appris au Chapitre 23). Une lambda est une petite fonction anonyme. L’expression lambda student: student[1] crée une fonction qui prend un tuple étudiant et renvoie son deuxième élément (le score).
37.5.6) Tri à plusieurs niveaux
Vous pouvez trier selon plusieurs critères en renvoyant un tuple depuis la fonction key. Python compare les tuples élément par élément, de gauche à droite :
Comment fonctionne la comparaison de tuples :
Lorsque Python compare deux tuples, il suit ces règles :
- Comparer les premiers éléments. S’ils sont différents, la comparaison est terminée.
- Si les premiers éléments sont égaux, comparer les deuxièmes éléments.
- Si les deuxièmes éléments sont égaux, comparer les troisièmes éléments.
- Continuer jusqu’à trouver une différence ou jusqu’à épuisement des éléments.
# Exemples de comparaison de tuples
print((1, 'a') < (2, 'z')) # Output: True (1 < 2, so True immediately)
print((1, 'z') < (1, 'a')) # Output: False (1 == 1, so compare 'z' < 'a')
print((1, 'a') < (1, 'a')) # Output: False (both tuples are equal)
print((1, 2, 9) < (1, 3, 1)) # Output: True (1 == 1, then 2 < 3)Cela rend les tuples parfaits pour un tri à plusieurs niveaux — Python gère automatiquement la logique « comparer le premier critère, puis le deuxième, puis le troisième » pour vous :
# Trier selon plusieurs critères
students = [
("Alice", "Smith", 92),
("Bob", "Jones", 85),
("Alice", "Brown", 88),
("Charlie", "Smith", 85)
]
# Trier par prénom, puis par nom
by_name = sorted(students, key=lambda s: (s[0], s[1]))
print("By name:")
for student in by_name:
print(f" {student}")
# Output:
# By name:
# ('Alice', 'Brown', 88)
# ('Alice', 'Smith', 92)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)
# Trier par score décroissant, puis par prénom croissant
by_score_then_name = sorted(students, key=lambda s: (-s[2], s[0]))
print("\nBy score (high to low), then name:")
for student in by_score_then_name:
print(f" {student}")
# Output:
# By score (high to low), then name:
# ('Alice', 'Smith', 92)
# ('Alice', 'Brown', 88)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)Remarque : Pour trier un critère en décroissant et un autre en croissant, nous nions la valeur numérique (-s[2]). Cela fonctionne parce que la négation inverse l’ordre de tri pour les nombres. Dans l’exemple ci-dessus, -s[2] trie les scores du plus grand au plus petit, tandis que s[0] trie les prénoms de A à Z.
37.5.7) Utiliser des fonctions utilitaires pour des clés complexes
Lorsque la logique de tri devient complexe, définir une fonction utilitaire rend le code plus lisible et plus facile à maintenir. Vous pouvez ensuite utiliser cette fonction utilitaire dans votre fonction key.
Exemple : trier des fichiers par extension
Supposons que vous vouliez regrouper les fichiers par leur extension (.csv, .jpg, .pdf, etc.), et au sein de chaque groupe, trier alphabétiquement par nom de fichier. La fonction key doit extraire l’extension du fichier, ce qui nécessite une manipulation de chaînes.
# Trier les fichiers par extension, puis par nom
files = [
"report.pdf",
"data.csv",
"image.jpg",
"notes.txt",
"backup.csv",
"photo.jpg"
]
# Extraire l’extension pour le tri
def get_extension(filename):
"""Extraire l’extension de fichier à partir d’un nom de fichier."""
return filename.split(".")[-1] # Découper sur "." et prendre la dernière partie
# Utiliser la fonction utilitaire dans la clé
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))
print("Files sorted by extension, then name:")
for file in sorted_files:
print(f" {file}")
# Output:
# Files sorted by extension, then name:
# backup.csv # csv files first (alphabetically)
# data.csv # csv files first (alphabetically)
# image.jpg # jpg files next
# photo.jpg # jpg files next
# report.pdf # pdf files next
# notes.txt # txt files lastComment cela fonctionne :
- La fonction key
lambda f: (get_extension(f), f)renvoie un tuple pour chaque nom de fichier - Pour "report.pdf", elle renvoie
("pdf", "report.pdf") - Pour "data.csv", elle renvoie
("csv", "data.csv") - Python trie par le premier élément du tuple (extension), puis par le second élément (nom de fichier complet)
- Cela regroupe les fichiers par extension et trie alphabétiquement au sein de chaque groupe
Pourquoi utiliser une fonction utilitaire ?
Comparez la lisibilité :
# Sans fonction utilitaire - plus difficile à comprendre
sorted_files = sorted(files, key=lambda f: (f.split(".")[-1], f))
# Avec fonction utilitaire - intention plus claire
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))La fonction utilitaire rend votre code auto-documenté. Toute personne lisant get_extension(f) comprend immédiatement ce qui se passe, tandis que f.split(".")[-1] demande une analyse mentale.
37.5.8) sorted() vs .sort() : quand utiliser chacun
Python propose deux façons de trier :
sorted()- Fonction qui renvoie une nouvelle liste triée.sort()- Méthode de liste qui trie sur place
# sorted() - crée une nouvelle liste, l’originale est inchangée
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(f"Original: {numbers}") # Output: Original: [3, 1, 4, 1, 5]
print(f"Sorted: {sorted_numbers}") # Output: Sorted: [1, 1, 3, 4, 5]
# .sort() - modifie la liste sur place, renvoie None
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(f"Modified: {numbers}") # Output: Modified: [1, 1, 3, 4, 5]
print(f"Return value: {result}") # Output: Return value: NoneQuand utiliser sorted() :
- Vous devez conserver l’ordre original
- Vous triez autre chose qu’une liste (tuple, chaîne, ensemble, etc.)
- Vous voulez trier et affecter en une seule expression
Quand utiliser .sort() :
- Vous avez une liste et vous n’avez pas besoin de l’ordre original
- Vous voulez économiser de la mémoire (aucune nouvelle liste créée)
- Vous triez une grande liste sur place pour l’efficacité
La fonction sorted() est l’un des outils les plus polyvalents de Python. Combinée au paramètre key, elle peut gérer pratiquement n’importe quel besoin de tri, du simple ordre numérique aux tris complexes multi-critères de structures de données imbriquées.
Ce chapitre vous a doté des fonctions intégrées et outils essentiels de Python. Vous avez appris à :
- Comprendre la hiérarchie des types de Python et prévoir quelles opérations fonctionnent avec quels types
- Utiliser des fonctions fondamentales comme
len(),sum(),min(),max(),abs()etround()pour des opérations courantes - Itérer avec des informations de position en utilisant
enumerate() - Traiter simultanément des séquences parallèles avec
zip() - Prendre des décisions sur des collections avec
any()etall() - Trier les données de manière flexible avec
sorted()et des fonctions key personnalisées
Ces outils constituent la base d’un code Python idiomatique. Ils sont efficaces, lisibles et gèrent correctement les cas limites. Au fil de votre progression, vous vous surprendrez à utiliser ces fonctions constamment — ce sont les briques de base qui rendent le code Python élégant et expressif.
Dans le prochain chapitre, nous explorerons les décorateurs (decorators), qui vous permettent de modifier et d’améliorer le comportement des fonctions de manière puissante, en s’appuyant sur les concepts de fonctions de première classe que nous avons appris au Chapitre 23.