15. Tuples et ranges : séquences immuables simples
Au chapitre 14, nous avons exploré les listes — le type de séquence mutable polyvalent de Python. Nous allons maintenant examiner deux autres types de séquences importants : les tuples et les ranges. Alors que les listes excellent pour stocker des collections qui changent au fil du temps, les tuples fournissent des séquences immuables qui protègent les données de toute modification, et les ranges offrent des moyens économes en mémoire de représenter des suites de nombres.
Comprendre quand utiliser chaque type de séquence rendra vos programmes plus efficaces, plus sûrs et plus explicites dans leur intention. À la fin de ce chapitre, vous saurez comment travailler efficacement avec les tuples et les ranges, et vous comprendrez les opérations courantes qui fonctionnent sur tous les types de séquences Python.
15.1) Créer et utiliser des tuples (l’importance de la virgule)
Un tuple est une séquence d’éléments ordonnée et immuable. Comme les listes, les tuples peuvent contenir n’importe quel type de données et conservent l’ordre des éléments. Cependant, contrairement aux listes, une fois que vous créez un tuple, vous ne pouvez pas modifier son contenu.
Créer des tuples avec des parenthèses
La façon la plus courante de créer un tuple consiste à entourer des valeurs séparées par des virgules entre parenthèses :
# Un tuple de notes de tests d'étudiants
scores = (85, 92, 78, 95)
print(scores) # Output: (85, 92, 78, 95)
print(type(scores)) # Output: <class 'tuple'>
# Un tuple de types de données mixtes
student_info = ("Alice", 20, "Computer Science", 3.8)
print(student_info) # Output: ('Alice', 20, 'Computer Science', 3.8)
# Un tuple vide
empty = ()
print(empty) # Output: ()
print(len(empty)) # Output: 0Les tuples utilisent les parenthèses () pour leur syntaxe littérale, tandis que les listes utilisent les crochets []. Cette distinction visuelle vous aide à reconnaître immédiatement avec quel type vous travaillez.
C’est la virgule qui crée le tuple, pas les parenthèses
Voici un détail crucial qui surprend beaucoup de débutants : c’est la virgule qui crée réellement un tuple, pas les parenthèses. Les parenthèses sont souvent facultatives et servent surtout à rendre le tuple plus visible ou à le regrouper dans des expressions.
# Tout ceci crée le même tuple
coordinates_1 = (10, 20)
coordinates_2 = 10, 20 # Pas besoin de parenthèses !
print(coordinates_1) # Output: (10, 20)
print(coordinates_2) # Output: (10, 20)
print(coordinates_1 == coordinates_2) # Output: True
# C'est la virgule qui compte
x = (42) # Ceci n'est que l'entier 42 entre parenthèses
y = (42,) # Ceci est un tuple contenant un seul élément
print(type(x)) # Output: <class 'int'>
print(type(y)) # Output: <class 'tuple'>
print(y) # Output: (42,)Les parenthèses dans (42) ne servent qu’à regrouper, comme en mathématiques. Pour créer un tuple à un seul élément, vous devez inclure une virgule finale : (42,). Cette virgule indique à Python que vous voulez un tuple, et pas seulement une expression regroupée.
Quand les parenthèses sont nécessaires
Même si la virgule crée le tuple, les parenthèses deviennent nécessaires dans certaines situations pour éviter toute ambiguïté :
# Sans parenthèses, ce serait déroutant
def get_dimensions():
return 1920, 1080 # Retourne un tuple
width, height = get_dimensions()
print(f"Screen: {width}x{height}") # Output: Screen: 1920x1080
# Parenthèses nécessaires lors du passage de tuples en arguments de fonction
print((1, 2, 3)) # Output: (1, 2, 3)
# Sans parenthèses, Python verrait trois arguments séparés
# Parenthèses nécessaires dans des expressions complexes
result = (10, 20) + (30, 40) # Concaténation de tuples
print(result) # Output: (10, 20, 30, 40)Créer des tuples à un seul élément
L’exigence de la virgule finale pour les tuples à un seul élément prend souvent les débutants au dépourvu :
# Erreur courante : oublier la virgule
not_a_tuple = ("Python")
print(type(not_a_tuple)) # Output: <class 'str'>
print(not_a_tuple) # Output: Python
# Correct : inclure la virgule finale
is_a_tuple = ("Python",)
print(type(is_a_tuple)) # Output: <class 'tuple'>
print(is_a_tuple) # Output: ('Python',)
# La virgule fonctionne même sans parenthèses
also_a_tuple = "Python",
print(type(also_a_tuple)) # Output: <class 'tuple'>
print(also_a_tuple) # Output: ('Python',)Pourquoi Python impose-t-il cette syntaxe apparemment maladroite ? Parce que les parenthèses ont déjà une autre signification en Python — elles servent à regrouper des expressions. Sans la virgule, Python n’a aucun moyen de distinguer (42) comme un nombre regroupé et (42) comme un tuple.
Accéder aux éléments d’un tuple
Les tuples prennent en charge les mêmes opérations d’indexation et de découpage que les listes :
# Tuple d'informations sur un étudiant
student = ("Bob", 22, "Physics", 3.6)
# Accès aux éléments individuels (indexation à partir de zéro)
name = student[0]
age = student[1]
major = student[2]
gpa = student[3]
print(f"{name} is {age} years old") # Output: Bob is 22 years old
print(f"Major: {major}, GPA: {gpa}") # Output: Major: Physics, GPA: 3.6
# L'indexation négative fonctionne aussi
last_item = student[-1]
print(f"Last item: {last_item}") # Output: Last item: 3.6
# Le découpage extrait un nouveau tuple
first_two = student[:2]
print(first_two) # Output: ('Bob', 22)
print(type(first_two)) # Output: <class 'tuple'>Toutes les techniques d’indexation et de découpage que vous avez apprises avec les listes au chapitre 14 fonctionnent à l’identique avec les tuples. La différence clé est que les tuples ne peuvent pas être modifiés après leur création.
15.2) Empaquetage et dépaquetage de tuples
L’une des fonctionnalités les plus puissantes et élégantes des tuples est leur capacité à empaqueter plusieurs valeurs ensemble et à les déballer dans des variables séparées. Cette fonctionnalité rend le code Python remarquablement concis et lisible.
Empaquetage de tuples
L’empaquetage (packing) de tuples se produit lorsque vous créez un tuple en plaçant plusieurs valeurs ensemble, séparées par des virgules :
# Empaquetage de valeurs dans un tuple
coordinates = 10, 20, 30
print(coordinates) # Output: (10, 20, 30)
# Empaquetage de types différents
user_data = "Alice", 25, "alice@example.com"
print(user_data) # Output: ('Alice', 25, 'alice@example.com')
# Empaquetage des valeurs de retour d'une fonction
def get_statistics(numbers):
total = sum(numbers)
count = len(numbers)
average = total / count
return total, count, average # Empaquette trois valeurs dans un tuple
stats = get_statistics([85, 90, 78, 92, 88])
print(stats) # Output: (433, 5, 86.6)Quand une fonction renvoie plusieurs valeurs séparées par des virgules, Python les empaquette automatiquement dans un tuple. C’est pourquoi les fonctions peuvent sembler renvoyer plusieurs valeurs — en réalité, elles renvoient un seul tuple contenant ces valeurs.
Dépaquetage de tuples
Le dépaquetage (unpacking) de tuples est le processus inverse : extraire des valeurs d’un tuple dans des variables séparées :
# Unpacking de base
point = (100, 200)
x, y = point
print(f"x = {x}, y = {y}") # Output: x = 100, y = 200
# L'unpacking fonctionne avec n'importe quelle séquence, pas seulement les tuples
name, age, email = ["Bob", 30, "bob@example.com"]
print(f"{name} is {age} years old") # Output: Bob is 30 years old
# Unpacking direct des valeurs de retour d'une fonction
total, count, average = get_statistics([95, 88, 92, 85])
print(f"Average of {count} scores: {average}") # Output: Average of 4 scores: 90.0Le nombre de variables à gauche doit correspondre au nombre d’éléments dans la séquence. S’ils ne correspondent pas, Python lève une ValueError :
# Ceci provoquera une erreur
coordinates = (10, 20, 30)
# x, y = coordinates # ValueError: too many values to unpack (expected 2)
# Ceci provoquera aussi une erreur
point = (5, 10)
# x, y, z = point # ValueError: not enough values to unpack (expected 3, got 2)Échanger des variables avec le dépaquetage de tuples
Le dépaquetage de tuples permet une manière élégante d’échanger des valeurs de variables sans avoir besoin d’une variable temporaire :
# Échange traditionnel avec une variable temporaire
a = 10
b = 20
temp = a
a = b
b = temp
print(f"a = {a}, b = {b}") # Output: a = 20, b = 10
# Échange élégant en Python via l'unpacking de tuples
x = 100
y = 200
x, y = y, x # Échange en une ligne !
print(f"x = {x}, y = {y}") # Output: x = 200, y = 100
# Échanger plus de deux variables
first = "A"
second = "B"
third = "C"
first, second, third = third, first, second
print(first, second, third) # Output: C A BComment cela fonctionne-t-il ? Python évalue d’abord le côté droit, en créant un tuple (y, x), puis le décompose dans les variables du côté gauche. Cela se produit en une seule étape, donc aucune variable temporaire n’est nécessaire.
Dépaquetage étendu avec l’opérateur étoile
Python fournit le dépaquetage étendu via l’opérateur * pour capturer plusieurs éléments :
# Unpacking avec une variable "reste"
scores = (95, 88, 92, 85, 90, 87)
first, second, *rest = scores
print(f"Top two: {first}, {second}") # Output: Top two: 95, 88
print(f"Others: {rest}") # Output: Others: [92, 85, 90, 87]
print(type(rest)) # Output: <class 'list'>
# L'étoile peut apparaître n'importe où
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(f"First: {first}") # Output: First: 1
print(f"Middle: {middle}") # Output: Middle: [2, 3, 4]
print(f"Last: {last}") # Output: Last: 5
# Capturer le début
*beginning, second_last, last = numbers
print(f"Beginning: {beginning}") # Output: Beginning: [1, 2, 3]
print(f"Last two: {second_last}, {last}") # Output: Last two: 4, 5Notez que la variable précédée de l’étoile capture toujours les éléments sous forme de liste, même lors d’un unpacking depuis un tuple. S’il n’y a aucun élément à capturer, la variable étoilée devient une liste vide :
# Lorsqu'il n'y a rien à capturer
a, b, *rest = (10, 20)
print(rest) # Output: []
# Une seule étoile autorisée par unpacking
# first, *middle, *end = (1, 2, 3, 4) # SyntaxError: multiple starred expressionsIgnorer des valeurs avec le tiret bas
Parfois, vous n’avez besoin que de certaines valeurs dans un tuple. Par convention, les développeurs Python utilisent le tiret bas _ comme nom de variable pour indiquer les valeurs qu’ils veulent ignorer :
# Analyse d'une chaîne de date
date_string = "2024-03-15"
year, month, day = date_string.split("-")
print(f"Month: {month}") # Output: Month: 03
# Si seul le mois nous intéresse
_, month, _ = date_string.split("-")
print(f"Month: {month}") # Output: Month: 03
# Avec unpacking étendu
data = ("Alice", 25, "Engineer", "New York", "alice@example.com")
name, age, *_, email = data
print(f"{name} ({age}): {email}") # Output: Alice (25): alice@example.comLe tiret bas n’est qu’un nom de variable ordinaire, mais son utilisation indique aux autres programmeurs (et à vous-même) que vous ignorez intentionnellement ces valeurs.
Exemples pratiques d’empaquetage et de dépaquetage
# Renvoyer plusieurs valeurs issues de calculs
def calculate_rectangle_properties(width, height):
"""Calculer l'aire et le périmètre d'un rectangle."""
area = width * height
perimeter = 2 * (width + height)
return area, perimeter # Packing
# Unpacking des résultats
rect_area, rect_perimeter = calculate_rectangle_properties(5, 3)
print(f"Area: {rect_area}, Perimeter: {rect_perimeter}") # Output: Area: 15, Perimeter: 16
# Itérer avec unpacking
students = [
("Alice", 85),
("Bob", 92),
("Carol", 78)
]
for name, score in students: # Unpacking dans la boucle
print(f"{name}: {score}")
# Output:
# Alice: 85
# Bob: 92
# Carol: 78L’empaquetage et le dépaquetage de tuples rendent le code Python plus lisible et expressif. Au lieu d’accéder aux éléments d’un tuple par index (student[0], student[1]), vous pouvez les déballer dans des variables aux noms explicites.
15.3) Les tuples sont immuables : quand c’est utile
La caractéristique définissante des tuples est leur immutabilité — une fois créé, le contenu d’un tuple ne peut plus être modifié. Vous ne pouvez pas ajouter, supprimer ou modifier des éléments. Cette immutabilité peut sembler être une limitation, mais elle apporte des avantages importants.
Ce que signifie l’immutabilité en pratique
# Créer un tuple
coordinates = (10, 20, 30)
print(coordinates) # Output: (10, 20, 30)
# Tenter de modifier lève une erreur
# coordinates[0] = 15 # TypeError: 'tuple' object does not support item assignment
# Tenter d'ajouter des éléments lève une erreur
# coordinates.append(40) # AttributeError: 'tuple' object has no attribute 'append'
# Tenter de supprimer des éléments lève une erreur
# del coordinates[1] # TypeError: 'tuple' object doesn't support item deletionQuand Python dit que les tuples ne prennent pas en charge l’affectation d’éléments, cela signifie que vous ne pouvez pas changer ce qui est stocké à une position quelconque dans le tuple. La structure du tuple est fixée lors de la création.
Comparer les listes mutables et les tuples immuables
# Les listes sont mutables - vous pouvez les modifier
shopping_list = ["milk", "bread", "eggs"]
shopping_list[1] = "butter" # Modifier un élément
shopping_list.append("cheese") # Ajouter un élément
print(shopping_list) # Output: ['milk', 'butter', 'eggs', 'cheese']
# Les tuples sont immuables - vous ne pouvez pas les modifier
product_dimensions = (10, 20, 5) # width, height, depth in cm
# product_dimensions[0] = 12 # TypeError: cannot modify
# product_dimensions.append(3) # AttributeError: no append method
# Pour "modifier" un tuple, vous devez en créer un nouveau
new_dimensions = (12, 20, 5) # Créer un tuple entièrement nouveau
print(new_dimensions) # Output: (12, 20, 5)Pourquoi l’immutabilité est utile
L’immutabilité offre plusieurs avantages pratiques :
1. Intégrité et sécurité des données
Quand vous passez un tuple à une fonction, vous savez que la fonction ne peut pas modifier vos données par accident :
def calculate_distance(point1, point2):
"""Calculer la distance entre deux points 2D."""
x1, y1 = point1
x2, y2 = point2
dx = x2 - x1
dy = y2 - y1
# Même si nous le voulions, nous ne pouvons pas modifier les tuples d'entrée
return (dx**2 + dy**2) ** 0.5
start = (0, 0)
end = (3, 4)
distance = calculate_distance(start, end)
print(f"Distance: {distance}") # Output: Distance: 5.0
print(f"Start point unchanged: {start}") # Output: Start point unchanged: (0, 0)Avec les listes, vous devriez vous demander si une fonction pourrait modifier vos données. Avec les tuples, vous avez la garantie que non.
2. Utiliser des tuples comme clés de dictionnaire
Comme nous l’explorerons davantage au chapitre 17, les clés de dictionnaire doivent être hachables — elles doivent avoir une valeur de hachage qui ne change jamais. Les objets immuables comme les tuples peuvent servir de clés de dictionnaire ; les objets mutables comme les listes ne le peuvent pas :
# Les tuples peuvent être des clés de dictionnaire
locations = {
(0, 0): "Origin",
(10, 20): "Point A",
(30, 40): "Point B"
}
print(locations[(10, 20)]) # Output: Point A
# Les listes ne peuvent pas être des clés de dictionnaire
# locations_bad = {
# [0, 0]: "Origin" # TypeError: unhashable type: 'list'
# }3. Signaler l’intention
Utiliser un tuple plutôt qu’une liste communique aux autres programmeurs (et à vous-même) que ces données ne doivent pas changer :
# Valeurs de couleur RGB - elles ne doivent jamais changer
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Paramètres de connexion à une base de données - configuration fixe
DB_CONFIG = ("localhost", 5432, "myapp", "production")
# Coordonnées géographiques - un lieu ne change pas
EIFFEL_TOWER = (48.8584, 2.2945) # latitude, longitudeQuand vous voyez un tuple dans du code, vous savez immédiatement que ces données sont censées rester constantes. Quand vous voyez une liste, vous savez qu’elle pourrait être modifiée.
4. Avantages de performance
Parce que les tuples sont immuables, Python peut les optimiser d’une manière qu’il ne peut pas appliquer aux listes. Nous apprendrons le module sys au chapitre 27, mais pour l’instant, sachez simplement que sys.getsizeof() nous indique combien de mémoire un objet utilise :
import sys
# Les tuples utilisent moins de mémoire que des listes équivalentes
tuple_data = (1, 2, 3, 4, 5)
list_data = [1, 2, 3, 4, 5]
print(f"Tuple size: {sys.getsizeof(tuple_data)} bytes") # Output: Tuple size: 80 bytes (may vary by Python version)
print(f"List size: {sys.getsizeof(list_data)} bytes") # Output: List size: 104 bytes (may vary by Python version)
# La création de tuples est plus rapide
import timeit
tuple_time = timeit.timeit("(1, 2, 3, 4, 5)", number=1000000)
list_time = timeit.timeit("[1, 2, 3, 4, 5]", number=1000000)
print(f"Tuple creation: {tuple_time:.4f} seconds")
print(f"List creation: {list_time:.4f} seconds")
# Example output: Tuple creation: 0.0055 seconds, List creation: 0.0292 seconds15.4) Le piège de l’immutabilité : quand les tuples contiennent des éléments mutables
Même si les tuples eux-mêmes sont immuables, ils peuvent contenir des objets mutables comme des listes ou des dictionnaires. Cela crée une distinction subtile mais importante : la structure du tuple est fixe, mais le contenu des objets mutables qu’il contient peut toujours changer.
Comprendre la distinction
# Un tuple contenant une liste
student_data = ("Alice", 20, [85, 90, 78]) # name, age, scores
print(student_data) # Output: ('Alice', 20, [85, 90, 78])
# Nous ne pouvons pas réaffecter les éléments du tuple
# student_data[0] = "Bob" # TypeError: 'tuple' object does not support item assignment
# Mais nous POUVONS modifier la liste à l'intérieur du tuple
student_data[2].append(92) # Ajouter une nouvelle note
print(student_data) # Output: ('Alice', 20, [85, 90, 78, 92])
student_data[2][0] = 88 # Modifier une note existante
print(student_data) # Output: ('Alice', 20, [88, 90, 78, 92])Que se passe-t-il ici ? Le tuple stocke trois références : une vers la chaîne "Alice", une vers l’entier 20, et une vers un objet liste. La structure du tuple — quels objets il référence — ne peut pas changer. Mais l’objet liste lui-même est mutable, donc son contenu peut changer.
Visualiser la différence
# La structure du tuple est fixe
data = ("Python", [1, 2, 3])
# Ceci essaie de changer ce que le tuple référence - INTERDIT
# data[1] = [4, 5, 6] # TypeError
# Ceci modifie la liste que le tuple référence - AUTORISÉ
data[1].append(4)
print(data) # Output: ('Python', [1, 2, 3, 4])
# Le tuple référence toujours le même objet liste
# Seul le contenu de la liste a changé, pas la liste vers laquelle le tuple pointeTuples avec des dictionnaires
Le même principe s’applique aux dictionnaires à l’intérieur des tuples :
# Tuple contenant un dictionnaire
user_profile = ("alice", {"email": "alice@example.com", "age": 25})
print(user_profile) # Output: ('alice', {'email': 'alice@example.com', 'age': 25})
# Impossible de changer quel dictionnaire le tuple référence
# user_profile[1] = {"email": "newemail@example.com"} # TypeError
# Mais on PEUT modifier le dictionnaire lui-même
user_profile[1]["age"] = 26
user_profile[1]["city"] = "New York"
print(user_profile) # Output: ('alice', {'email': 'alice@example.com', 'age': 26, 'city': 'New York'})Pourquoi cela compte pour les clés de dictionnaire
Les tuples peuvent être utilisés comme clés de dictionnaire uniquement si tous leurs éléments sont hachables. Bien que les tuples eux-mêmes soient immuables, un tuple contenant des objets mutables (comme des listes) n’est pas hachable du tout et ne peut donc pas être utilisé comme clé de dictionnaire.
# Cela fonctionne mais c'est dangereux
tuple_with_list = ("key", [1, 2, 3])
# data = {tuple_with_list: "value"} # TypeError: unhashable type: 'list'N’utilisez comme clés de dictionnaire que des tuples contenant des objets entièrement immuables (chaînes, nombres, frozensets, autres tuples).
Créer des tuples vraiment immuables
Si vous avez besoin d’un tuple complètement immuable, assurez-vous que tout son contenu est également immuable :
# Tuple entièrement immuable - uniquement des types immuables
point_3d = (10, 20, 30) # Tous des entiers
rgb_color = (255, 128, 0) # Tous des entiers
coordinates = ((10, 20), (30, 40)) # Tuple de tuples
# Ceux-ci sont sûrs comme clés de dictionnaire
color_names = {
(255, 0, 0): "Red",
(0, 255, 0): "Green",
(0, 0, 255): "Blue"
}
# Les tuples imbriqués restent immuables
nested = ((1, 2), (3, 4))
# nested[0][0] = 5 # TypeError: 'tuple' object does not support item assignmentQuand les contenus mutables sont intentionnels
Parfois, vous voulez réellement un tuple avec un contenu mutable — par exemple, lorsque vous avez une structure d’enregistrement fixe mais qu’un champ doit changer :
# Dossier étudiant avec identité fixe mais notes qui évoluent
def create_student(name, student_id):
"""Créer un dossier étudiant avec une liste de notes vide."""
return (name, student_id, []) # name et ID fixes, notes modifiables
student = create_student("Alice", "S12345")
print(student) # Output: ('Alice', 'S12345', [])
# L'identité de l'étudiant est fixe
print(f"Student: {student[0]} (ID: {student[1]})") # Output: Student: Alice (ID: S12345)
# Mais on peut ajouter des notes au fur et à mesure
student[2].append(85)
student[2].append(92)
student[2].append(78)
print(f"Grades: {student[2]}") # Output: Grades: [85, 92, 78]
# La structure du tuple protège le nom et l'ID contre des changements accidentels
# tout en permettant à la liste de notes de s'agrandirCe modèle est utile quand vous voulez protéger certaines données tout en permettant à d’autres de changer. Faites simplement attention à la distinction entre l’immutabilité du tuple et la mutabilité de son contenu.
15.5) Quand utiliser des tuples plutôt que des listes
Choisir entre tuples et listes est une décision de conception importante. Bien que ce soient tous deux des séquences, ils servent des objectifs différents et communiquent des intentions différentes.
Utiliser des tuples pour des données fixes et hétérogènes
Les tuples fonctionnent mieux lorsque vous avez un nombre fixe d’éléments qui représentent une seule entité logique, souvent avec différents types :
# Dossier étudiant : nom, âge, filière, GPA
student = ("Alice", 20, "Computer Science", 3.8)
# Coordonnées géographiques : latitude, longitude
location = (40.7128, -74.0060) # New York City
# Couleur RGB : rouge, vert, bleu
color = (255, 128, 0)
# Connexion base de données : host, port, database, username
db_connection = ("localhost", 5432, "myapp", "admin")
# Date : année, mois, jour
date = (2024, 3, 15)Chaque tuple représente un « enregistrement » complet où la position de chaque élément a une signification précise. Le premier élément est toujours le nom, le second est toujours l’âge, et ainsi de suite.
Utiliser des listes pour des collections homogènes
Les listes fonctionnent mieux lorsque vous avez un nombre variable d’éléments similaires que vous pourriez ajouter, supprimer ou réordonner :
# Liste de courses - éléments du même type (chaînes)
shopping_list = ["milk", "bread", "eggs", "butter"]
shopping_list.append("cheese") # Ajouter des éléments si nécessaire
shopping_list.remove("bread") # Supprimer des éléments
# Notes de tests - éléments du même type (nombres)
test_scores = [85, 92, 78, 95, 88]
test_scores.append(90) # Ajouter une nouvelle note
test_scores.sort() # Réordonner les notes
# Noms d'utilisateurs - éléments du même type (chaînes)
active_users = ["alice", "bob", "carol"]
active_users.extend(["dave", "eve"]) # Ajouter plusieurs utilisateursLes listes servent aux collections dont la taille peut changer et où chaque élément joue le même rôle.
Tuples pour les valeurs de retour des fonctions
Quand une fonction renvoie plusieurs valeurs liées, les tuples sont le choix naturel :
def get_user_info(user_id):
"""Récupérer les informations utilisateur depuis la base de données."""
# Simuler une recherche dans la base
return "Alice", "alice@example.com", 25, "New York"
# Déballer le tuple renvoyé
name, email, age, city = get_user_info(101)
print(f"{name} from {city}") # Output: Alice from New York
def calculate_statistics(numbers):
"""Calculer le min, le max et la moyenne de numbers."""
if not numbers:
return None, None, None
minimum = min(numbers)
maximum = max(numbers)
average = sum(numbers) / len(numbers)
return minimum, maximum, average
# Déballer les résultats
min_val, max_val, avg_val = calculate_statistics([85, 92, 78, 95, 88])
print(f"Range: {min_val} to {max_val}, Average: {avg_val}")
# Output: Range: 78 to 95, Average: 87.6Renvoyer des tuples clarifie que ces valeurs sont liées et doivent être considérées ensemble.
Tuples pour les clés de dictionnaire
Quand vous avez besoin de clés composites dans un dictionnaire, les tuples sont essentiels :
# Notes des étudiants par cours et semestre
grades = {
("CS101", "Fall2023"): 85,
("CS101", "Spring2024"): 90,
("MATH201", "Fall2023"): 88,
("MATH201", "Spring2024"): 92
}
# Rechercher une note précise
course = "CS101"
semester = "Spring2024"
grade = grades[(course, semester)]
print(f"Grade in {course} ({semester}): {grade}") # Output: Grade in CS101 (Spring2024): 90
# Coordonnées de grille comme clés de dictionnaire
grid = {
(0, 0): "Start",
(5, 3): "Obstacle",
(10, 10): "Goal"
}
position = (5, 3)
if position in grid:
print(f"At {position}: {grid[position]}") # Output: At (5, 3): ObstacleLes listes ne peuvent pas être des clés de dictionnaire parce qu’elles sont mutables, mais les tuples le peuvent.
Tuples pour une configuration immuable
Quand vous avez des données de configuration qui ne doivent jamais changer, les tuples signalent cette intention :
# Paramètres d'application qui doivent rester constants
APP_CONFIG = (
"MyApp", # Nom de l'application
"1.0.0", # Version
"production", # Environnement
True, # Mode debug
8080 # Port
)
# Palette de couleurs pour l'UI - ces couleurs sont fixes
COLOR_PALETTE = (
(255, 0, 0), # Rouge primaire
(0, 128, 255), # Bleu primaire
(255, 255, 255), # Blanc
(0, 0, 0) # Noir
)
# Endpoints d'API - ces URL ne changent pas
API_ENDPOINTS = (
"https://api.example.com/users",
"https://api.example.com/products",
"https://api.example.com/orders"
)Guide de décision
# Utilisez des TUPLES quand :
# 1. Les données représentent un seul enregistrement à structure fixe
employee = ("E001", "Alice", "Engineering", 75000)
# 2. Vous renvoyez plusieurs valeurs depuis une fonction
def divide_with_remainder(a, b):
return a // b, a % b
# 3. Vous devez les utiliser comme clés de dictionnaire
cache = {(5, 10): 50, (3, 7): 21}
# 4. Les données ne doivent pas être modifiées
SCREEN_RESOLUTION = (1920, 1080)
# Utilisez des LISTES quand :
# 1. Collection d'éléments similaires susceptible de changer
tasks = ["Write code", "Test code", "Deploy code"]
tasks.append("Document code")
# 2. Vous devez ajouter, supprimer ou réordonner des éléments
scores = [85, 90, 78]
scores.sort()
scores.append(92)
# 3. Tous les éléments servent le même objectif
usernames = ["alice", "bob", "carol"]
# 4. La taille de la collection n'est pas connue à l'avance
results = []
for i in range(10):
results.append(i * 2)15.6) Comprendre en profondeur les objets range
Maintenant que nous comprenons quand utiliser des tuples plutôt que des listes, explorons le troisième type de séquence immuable de Python : les ranges. Le type range représente une séquence de nombres immuable. Contrairement aux listes et aux tuples qui stockent tous leurs éléments en mémoire, les objets range génèrent les nombres à la demande, ce qui les rend extrêmement économes en mémoire pour représenter de grandes séquences.
Créer des objets range
La fonction range() crée des objets range sous trois formes :
# Un argument : range(stop)
# Génère des nombres de 0 jusqu'à (sans inclure) stop
numbers = range(5)
print(list(numbers)) # Output: [0, 1, 2, 3, 4]
# Deux arguments : range(start, stop)
# Génère des nombres de start jusqu'à (sans inclure) stop
numbers = range(2, 7)
print(list(numbers)) # Output: [2, 3, 4, 5, 6]
# Trois arguments : range(start, stop, step)
# Génère des nombres de start jusqu'à stop, en incrémentant de step
numbers = range(0, 10, 2)
print(list(numbers)) # Output: [0, 2, 4, 6, 8]
# step négatif pour compter à rebours
numbers = range(10, 0, -1)
print(list(numbers)) # Output: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]Notez que nous convertissons les ranges en listes avec list() pour voir leur contenu. Un objet range ne montre pas toutes ses valeurs lorsqu’on l’affiche :
r = range(5)
print(r) # Output: range(0, 5)
print(type(r)) # Output: <class 'range'>Comment fonctionnent les objets range
Les objets range ne stockent pas toutes leurs valeurs en mémoire. Ils calculent plutôt chaque valeur quand on en a besoin :
import sys
# Un range représentant un million de nombres
large_range = range(1000000)
print(f"Range size: {sys.getsizeof(large_range)} bytes") # Output: Range size: 48 bytes (may vary by Python version)
# Une liste contenant un million de nombres
large_list = list(range(1000000))
print(f"List size: {sys.getsizeof(large_list)} bytes") # Output: List size: 8000056 bytes (approximately 8MB)
# Le range est minuscule ; la liste est énorme !Un objet range ne stocke que trois valeurs : start, stop et step. Il calcule chaque nombre de la séquence lorsque vous le demandez. Cela rend les ranges incroyablement efficaces pour de grandes séquences.
Utiliser range dans des boucles for
Comme nous l’avons appris au chapitre 12, les ranges sont le plus souvent utilisés avec des boucles for :
# Compter de 0 à 4
for i in range(5):
print(f"Count: {i}")
# Output:
# Count: 0
# Count: 1
# Count: 2
# Count: 3
# Count: 4
# Compter de 1 à 10
for i in range(1, 11):
print(i, end=" ")
print() # Output: 1 2 3 4 5 6 7 8 9 10
# Compter de deux en deux
for i in range(0, 20, 2):
print(i, end=" ")
print() # Output: 0 2 4 6 8 10 12 14 16 18
# Compter à rebours
for i in range(5, 0, -1):
print(f"T-minus {i}")
# Output:
# T-minus 5
# T-minus 4
# T-minus 3
# T-minus 2
# T-minus 1Indexation et découpage des objets range
Les objets range prennent en charge l’indexation et le découpage comme les autres séquences :
# Créer un range
numbers = range(10, 50, 5) # 10, 15, 20, 25, 30, 35, 40, 45
# Indexation
print(numbers[0]) # Output: 10
print(numbers[3]) # Output: 25
print(numbers[-1]) # Output: 45
# Le découpage renvoie un nouveau range
subset = numbers[2:5]
print(subset) # Output: range(20, 35, 5)
print(list(subset)) # Output: [20, 25, 30]
# Longueur
print(len(numbers)) # Output: 8Tester l’appartenance
Vous pouvez vérifier si un nombre appartient à un range avec l’opérateur in :
# Nombres pairs de 0 à 20
evens = range(0, 21, 2)
print(10 in evens) # Output: True
print(15 in evens) # Output: False
print(20 in evens) # Output: True
# C'est très efficace - Python ne génère pas tous les nombres
# Il calcule si le nombre serait dans la séquence
large_range = range(0, 1000000, 3)
print(999999 in large_range) # Output: True (instant, no iteration needed)Python peut déterminer l’appartenance mathématiquement sans générer tous les nombres, ce qui rend cette opération extrêmement rapide même pour d’énormes ranges.
Ranges vides et inversés
# Range vide - stop égal à start
empty = range(5, 5)
print(list(empty)) # Output: []
print(len(empty)) # Output: 0
# Range vide - impossible d'atteindre stop avec le step donné
impossible = range(1, 10, -1) # Can't count up with negative step
print(list(impossible)) # Output: []
# Range inversé
backwards = range(10, 0, -1)
print(list(backwards)) # Output: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
# Inversé avec des nombres négatifs
negative_range = range(-5, -15, -2)
print(list(negative_range)) # Output: [-5, -7, -9, -11, -13]Quand utiliser range plutôt que des listes
# Utilisez range quand :
# 1. Vous avez besoin d'une séquence de nombres pour itérer
for i in range(100):
# Traiter quelque chose 100 fois
pass
# 2. Vous avez besoin d'indices pour une séquence
items = ["a", "b", "c", "d"]
for i in range(len(items)):
print(f"Index {i}: {items[i]}")
# 3. L'efficacité mémoire compte avec de grandes séquences
# Ceci utilise une mémoire minimale
for i in range(1000000):
if i % 100000 == 0:
print(i)
# Utilisez des listes quand :
# 1. Vous avez besoin de stocker les valeurs réelles
squares = [1, 3, 5, 7, 10]
# 2. Vous devez modifier la séquence
numbers = list(range(5))
numbers[2] = 100 # Modifier une valeur
numbers.append(200) # Ajouter une valeur
# 3. Vous devez utiliser la séquence plusieurs fois avec des opérations différentes
data = list(range(10))
print(sum(data))
print(max(data))
print(sorted(data, reverse=True))Les objets range sont un exemple parfait de l’efficacité de Python. Ils offrent tous les avantages d’une séquence sans le coût mémoire consistant à stocker chaque élément.
15.7) Convertir entre listes, tuples et ranges
Python permet de convertir facilement entre différents types de séquences. Comprendre ces conversions vous aide à choisir le bon type pour chaque situation et à transformer les données selon les besoins.
Conversion vers des listes
La fonction list() convertit n’importe quelle séquence en liste :
# Tuple vers liste
student_tuple = ("Alice", 20, "CS")
student_list = list(student_tuple)
print(student_list) # Output: ['Alice', 20, 'CS']
print(type(student_list)) # Output: <class 'list'>
# Maintenant on peut la modifier
student_list[1] = 21
student_list.append(3.8)
print(student_list) # Output: ['Alice', 21, 'CS', 3.8]
# Range vers liste
numbers = range(5)
numbers_list = list(numbers)
print(numbers_list) # Output: [0, 1, 2, 3, 4]
# Chaîne vers liste (chaque caractère devient un élément)
text = "Python"
chars = list(text)
print(chars) # Output: ['P', 'y', 't', 'h', 'o', 'n']Convertir en liste est utile lorsque vous devez modifier une séquence ou lorsque vous avez besoin de méthodes spécifiques aux listes comme append(), sort() ou remove().
Conversion vers des tuples
La fonction tuple() convertit n’importe quelle séquence en tuple :
# Liste vers tuple
scores_list = [85, 90, 78, 92]
scores_tuple = tuple(scores_list)
print(scores_tuple) # Output: (85, 90, 78, 92)
print(type(scores_tuple)) # Output: <class 'tuple'>
# Maintenant c'est immuable
# scores_tuple[0] = 88 # TypeError: 'tuple' object does not support item assignment
# Range vers tuple
numbers = range(1, 6)
numbers_tuple = tuple(numbers)
print(numbers_tuple) # Output: (1, 2, 3, 4, 5)
# Chaîne vers tuple
text = "Hi"
chars_tuple = tuple(text)
print(chars_tuple) # Output: ('H', 'i')Convertir en tuple est utile lorsque vous voulez protéger des données contre toute modification ou lorsque vous devez utiliser une séquence comme clé de dictionnaire.
15.8) Opérations de séquence communes aux chaînes, listes, tuples et ranges
Les types de séquences de Python — chaînes, listes, tuples et ranges — partagent de nombreuses opérations communes. Comprendre ces opérations partagées vous aide à travailler efficacement avec n’importe quel type de séquence.
Longueur, minimum et maximum
Toutes les séquences prennent en charge les fonctions len(), min() et max() :
# Chaînes
text = "Python"
print(len(text)) # Output: 6
print(min(text)) # Output: P (smallest character by Unicode value)
print(max(text)) # Output: y (largest character by Unicode value)
# Listes
numbers = [45, 12, 78, 23, 56]
print(len(numbers)) # Output: 5
print(min(numbers)) # Output: 12
print(max(numbers)) # Output: 78
# Tuples
scores = (85, 92, 78, 95, 88)
print(len(scores)) # Output: 5
print(min(scores)) # Output: 78
print(max(scores)) # Output: 95
# Ranges
nums = range(10, 50, 5)
print(len(nums)) # Output: 8
print(min(nums)) # Output: 10
print(max(nums)) # Output: 45Pour que min() et max() fonctionnent, les éléments doivent être comparables. Vous ne pouvez pas trouver le minimum d’une liste contenant à la fois des chaînes et des nombres :
mixed = [1, "hello", 3]
# print(min(mixed)) # TypeError: '<' not supported between instances of 'str' and 'int'Indexation et indexation négative
Toutes les séquences prennent en charge l’indexation avec des indices positifs et négatifs :
# Indexation positive (base 0)
text = "Python"
numbers = [10, 20, 30, 40, 50]
coords = (5, 10, 15)
values = range(0, 100, 10)
print(text[0]) # Output: P
print(numbers[2]) # Output: 30
print(coords[1]) # Output: 10
print(values[3]) # Output: 30
# Indexation négative (depuis la fin)
print(text[-1]) # Output: n (last character)
print(numbers[-2]) # Output: 40 (second from end)
print(coords[-3]) # Output: 5 (third from end, which is first)
print(values[-1]) # Output: 90 (last value in range)Les indices négatifs comptent depuis la fin : -1 est le dernier élément, -2 l’avant-dernier, et ainsi de suite.
Test d’appartenance avec in et not in
Toutes les séquences prennent en charge le test d’appartenance :
# Chaînes - vérifie les sous-chaînes
text = "Python Programming"
print("Python" in text) # Output: True
print("Java" in text) # Output: False
print("gram" in text) # Output: True (substring)
print("PYTHON" not in text) # Output: True (case-sensitive)
# Listes
fruits = ["apple", "banana", "cherry", "date"]
print("banana" in fruits) # Output: True
print("grape" in fruits) # Output: False
print("apple" not in fruits) # Output: False
# Tuples
coordinates = (10, 20, 30, 40)
print(20 in coordinates) # Output: True
print(25 in coordinates) # Output: False
print(50 not in coordinates) # Output: True
# Ranges - très efficace, aucune itération nécessaire
numbers = range(0, 100, 2) # Even numbers 0 to 98
print(50 in numbers) # Output: True
print(51 in numbers) # Output: False (odd number)
print(100 in numbers) # Output: False (stop is exclusive)Pour les ranges, Python peut déterminer l’appartenance mathématiquement sans vérifier chaque élément, ce qui le rend extrêmement rapide même pour d’énormes ranges.
Concaténation et répétition
Les chaînes, les listes et les tuples prennent en charge la concaténation avec + et la répétition avec * :
# Concaténation avec +
text1 = "Hello"
text2 = " World"
print(text1 + text2) # Output: Hello World
list1 = [1, 2, 3]
list2 = [4, 5, 6]
print(list1 + list2) # Output: [1, 2, 3, 4, 5, 6]
tuple1 = (10, 20)
tuple2 = (30, 40)
print(tuple1 + tuple2) # Output: (10, 20, 30, 40)
# Répétition avec *
print("Ha" * 3) # Output: HaHaHa
print([0] * 5) # Output: [0, 0, 0, 0, 0]
print((1, 2) * 3) # Output: (1, 2, 1, 2, 1, 2)Important : les ranges ne prennent pas en charge la concaténation ni la répétition :
r1 = range(5)
r2 = range(5, 10)
# combined = r1 + r2 # TypeError: unsupported operand type(s) for +: 'range' and 'range'
# Pour combiner des ranges, convertissez d'abord en listes ou en tuples
combined = list(r1) + list(r2)
print(combined) # Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Compter les occurrences
La méthode count() renvoie le nombre de fois où un élément apparaît :
# Chaînes - compte les occurrences d'une sous-chaîne
text = "Mississippi"
print(text.count("s")) # Output: 4
print(text.count("ss")) # Output: 2
print(text.count("i")) # Output: 4
# Listes
numbers = [1, 2, 3, 2, 4, 2, 5]
print(numbers.count(2)) # Output: 3
print(numbers.count(6)) # Output: 0
# Tuples
grades = (85, 90, 85, 92, 85, 88)
print(grades.count(85)) # Output: 3
print(grades.count(95)) # Output: 0
# Les ranges n'ont pas de méthode count(), mais vous pouvez convertir d'abord
nums = range(0, 20, 2)
nums_list = list(nums)
print(nums_list.count(10)) # Output: 1Trouver l’index des éléments
La méthode index() renvoie la position de la première occurrence :
# Chaînes
text = "Python Programming"
print(text.index("P")) # Output: 0 (first P)
print(text.index("Pro")) # Output: 7 (substring position)
# print(text.index("Java")) # ValueError: substring not found
# Listes
fruits = ["apple", "banana", "cherry", "banana"]
print(fruits.index("banana")) # Output: 1 (first occurrence)
print(fruits.index("cherry")) # Output: 2
# print(fruits.index("grape")) # ValueError: 'grape' is not in list
# Tuples
coordinates = (10, 20, 30, 20, 40)
print(coordinates.index(20)) # Output: 1 (first occurrence)
print(coordinates.index(40)) # Output: 4
# Les ranges n'ont pas de méthode index(), mais vous pouvez convertir d'abord
nums = range(10, 50, 5)
nums_list = list(nums)
print(nums_list.index(25)) # Output: 3Si l’élément n’est pas trouvé, index() lève une ValueError. Pour éviter cela, vérifiez d’abord avec in :
fruits = ["apple", "banana", "cherry"]
search_fruit = "grape"
if search_fruit in fruits:
position = fruits.index(search_fruit)
print(f"{search_fruit} found at position {position}")
else:
print(f"{search_fruit} not found")
# Output: grape not foundItération avec des boucles for
Toutes les séquences peuvent être parcourues avec des boucles for :
# Chaînes - itérer sur les caractères
for char in "Python":
print(char, end=" ")
print() # Output: P y t h o n
# Listes
for fruit in ["apple", "banana", "cherry"]:
print(f"I like {fruit}")
# Output:
# I like apple
# I like banana
# I like cherry
# Tuples
for score in (85, 90, 78):
print(f"Score: {score}")
# Output:
# Score: 85
# Score: 90
# Score: 78
# Ranges
for i in range(1, 6):
print(f"Count: {i}")
# Output:
# Count: 1
# Count: 2
# Count: 3
# Count: 4
# Count: 5Opérations de comparaison
Les séquences peuvent être comparées avec ==, !=, <, >, <= et >= :
# Égalité
print([1, 2, 3] == [1, 2, 3]) # Output: True
print((1, 2, 3) == (1, 2, 3)) # Output: True
print("abc" == "abc") # Output: True
# Inégalité
print([1, 2, 3] != [1, 2, 4]) # Output: True
print((1, 2) != (1, 2)) # Output: False
# Comparaison lexicographique (élément par élément)
print([1, 2, 3] < [1, 2, 4]) # Output: True (3 < 4)
print([1, 2, 3] < [1, 3, 0]) # Output: True (2 < 3)
print("apple" < "banana") # Output: True (alphabetical)
print((1, 2) < (1, 2, 3)) # Output: True (shorter is less if equal so far)
# Comparer des types différents
print([1, 2, 3] == (1, 2, 3)) # Output: False (different types)La comparaison fonctionne élément par élément de gauche à droite. Le premier élément différent détermine le résultat.
Comprendre ces opérations communes vous permet d’écrire du code qui fonctionne avec n’importe quel type de séquence, rendant vos programmes plus flexibles et réutilisables.
15.9) Découpage avancé sur tous les types de séquences
Le découpage est l’une des fonctionnalités les plus puissantes de Python pour travailler avec des séquences. Bien que nous ayons introduit le découpage de base au chapitre 14, il existe des techniques avancées de découpage qui fonctionnent sur tous les types de séquences.
Rappel sur le découpage de base
Le découpage extrait une partie d’une séquence avec la syntaxe sequence[start:stop:step] :
# Découpage de base avec des chaînes
text = "Python Programming"
print(text[0:6]) # Output: Python
print(text[7:18]) # Output: Programming
print(text[7:]) # Output: Programming (from index 7 to end)
print(text[:6]) # Output: Python (from start to index 6)
# Découpage de base avec des listes
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:7]) # Output: [2, 3, 4, 5, 6]
print(numbers[:5]) # Output: [0, 1, 2, 3, 4]
print(numbers[5:]) # Output: [5, 6, 7, 8, 9]
# Découpage de base avec des tuples
coordinates = (10, 20, 30, 40, 50, 60)
print(coordinates[1:4]) # Output: (20, 30, 40)
print(coordinates[:3]) # Output: (10, 20, 30)
print(coordinates[3:]) # Output: (40, 50, 60)
# Découpage de base avec des ranges
nums = range(0, 100, 10)
print(list(nums[2:5])) # Output: [20, 30, 40]Rappelez-vous : start est inclusif, stop est exclusif, et le résultat est toujours du même type que la séquence d’origine.
Utiliser step dans le découpage
Le troisième paramètre facultatif step contrôle combien d’éléments sauter :
# Un élément sur deux
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[::2]) # Output: [0, 2, 4, 6, 8]
print(numbers[1::2]) # Output: [1, 3, 5, 7, 9]
# Un élément sur trois
text = "abcdefghijklmnop"
print(text[::3]) # Output: adgjmp
# step avec start et stop
print(numbers[2:8:2]) # Output: [2, 4, 6]
print(text[1:10:2]) # Output: bdfhjStep négatif : inverser des séquences
Un step négatif inverse le sens du découpage :
# Inverser des séquences entières
text = "Python"
print(text[::-1]) # Output: nohtyP
numbers = [1, 2, 3, 4, 5]
print(numbers[::-1]) # Output: [5, 4, 3, 2, 1]
coordinates = (10, 20, 30, 40)
print(coordinates[::-1]) # Output: (40, 30, 20, 10)
# Inversion avec step
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[::-2]) # Output: [9, 7, 5, 3, 1] (every second, backwards)
# Inverser une portion
text = "Python Programming"
print(text[7:18][::-1]) # Output: gnimmargorP (reverse "Programming")Lorsqu’on utilise un step négatif, start et stop fonctionnent différemment :
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Avec un step négatif, start doit être supérieur à stop
print(numbers[7:2:-1]) # Output: [7, 6, 5, 4, 3] (from 7 down to 3)
print(numbers[8:3:-2]) # Output: [8, 6, 4] (from 8 down to 4, step -2)
# Omettre start/stop avec un step négatif
print(numbers[:5:-1]) # Output: [9, 8, 7, 6] (from end down to 6)
print(numbers[5::-1]) # Output: [5, 4, 3, 2, 1, 0] (from 5 down to start)Indices négatifs dans le découpage
Vous pouvez utiliser des indices négatifs pour les positions start et stop :
text = "Python Programming"
# Les 11 derniers caractères
print(text[-11:]) # Output: Programming
# Tout sauf les 11 derniers caractères
print(text[:-11]) # Output: Python
# De -15 à -5
print(text[-15:-5]) # Output: hon Progra
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Les 5 derniers éléments
print(numbers[-5:]) # Output: [5, 6, 7, 8, 9]
# Tout sauf les 3 derniers éléments
print(numbers[:-3]) # Output: [0, 1, 2, 3, 4, 5, 6]
# De -7 à -2
print(numbers[-7:-2]) # Output: [3, 4, 5, 6, 7]Découpage avec des ranges
Découper un range renvoie un nouvel objet range :
# Découper des ranges
numbers = range(0, 100, 5) # 0, 5, 10, 15, ..., 95
print(numbers) # Output: range(0, 100, 5)
# Le slice renvoie un nouveau range
subset = numbers[5:10]
print(subset) # Output: range(25, 50, 5)
print(list(subset)) # Output: [25, 30, 35, 40, 45]
# Avec step
every_other = numbers[::2]
print(list(every_other)) # Output: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
# step négatif
reversed_range = numbers[::-1]
print(list(reversed_range)) # Output: [95, 90, 85, ..., 5, 0]Slices vides et cas limites
numbers = [1, 2, 3, 4, 5]
# Slices vides (start >= stop avec step positif)
print(numbers[3:3]) # Output: []
print(numbers[5:10]) # Output: [] (stop beyond length)
print(numbers[10:20]) # Output: [] (both beyond length)
# Les slices hors limites sont sûrs
print(numbers[-100:100]) # Output: [1, 2, 3, 4, 5] (entire sequence)
print(numbers[2:100]) # Output: [3, 4, 5] (from 2 to end)
# step négatif avec start/stop incompatibles
print(numbers[2:7:-1]) # Output: [] (can't go forward with negative step)
# Un step de 0 n'est pas autorisé
# print(numbers[::0]) # ValueError: slice step cannot be zeroDécoupage pour copier
Le découpage crée une nouvelle séquence, ce qui fournit un moyen de copier :
# Copier avec le découpage
original = [1, 2, 3, 4, 5]
copy = original[:] # Slice du début à la fin
print(copy) # Output: [1, 2, 3, 4, 5]
# Modifier la copie n'affecte pas l'original
copy[0] = 100
print(f"Original: {original}") # Output: Original: [1, 2, 3, 4, 5]
print(f"Copy: {copy}") # Output: Copy: [100, 2, 3, 4, 5]
# Cela fonctionne aussi pour les tuples (crée un nouveau tuple)
original_tuple = (1, 2, 3, 4, 5)
copy_tuple = original_tuple[:]
print(copy_tuple) # Output: (1, 2, 3, 4, 5)
# Pour les chaînes
text = "Python"
text_copy = text[:]
print(text_copy) # Output: PythonCependant, souvenez-vous du chapitre 14 : cela crée une copie superficielle.
# Limitation des copies superficielles
original = [[1, 2], [3, 4]]
copy = original[:]
# Modifier une liste imbriquée affecte les deux
copy[0][0] = 100
print(f"Original: {original}") # Output: Original: [[100, 2], [3, 4]]
print(f"Copy: {copy}") # Output: Copy: [[100, 2], [3, 4]]Les tuples et les ranges sont des outils essentiels dans la boîte à outils des séquences de Python. Les tuples fournissent des données structurées et immuables qui protègent les informations contre des modifications accidentelles et permettent leur utilisation comme clés de dictionnaire. Les ranges offrent des représentations économes en mémoire des suites de nombres, parfaites pour les boucles et les grandes séquences. Comprendre quand utiliser chaque type — et comment convertir entre eux — rend votre code plus efficace, plus sûr et plus explicite dans son intention.
Les opérations communes partagées par tous les types de séquences — indexation, découpage, itération, test d’appartenance — forment une interface cohérente qui rend le travail avec n’importe quelle séquence intuitif. Les techniques avancées de découpage vous donnent des moyens puissants et expressifs d’extraire et de manipuler des données de séquence.
Au fur et à mesure que vous continuerez à programmer en Python, vous vous surprendrez à choisir naturellement le bon type de séquence pour chaque situation : les listes pour des collections qui changent, les tuples pour des enregistrements fixes, les ranges pour des suites de nombres, et les chaînes pour du texte. Ce chapitre vous a donné les connaissances nécessaires pour faire ces choix avec confiance et utiliser chaque type efficacement.