Définition de range() en Python
La fonction range() est une fonction native de Python qui génère une séquence immuable de nombres entiers. Elle est principalement utilisée pour itérer un nombre défini de fois dans une boucle for, mais ses usages vont bien au-delà. Si vous souhaitez maîtriser toutes les subtilités de Python, y compris range(), n'hésitez pas à suivre notre formation complète sur Python qui couvre ce sujet en profondeur.
Contrairement à ce que l'on pourrait penser, range() ne retourne pas une list. Elle retourne un objet de type range, qui est un itérable paresseux (lazy iterable). Cela signifie que les nombres ne sont pas tous stockés en mémoire simultanément : ils sont générés à la demande, ce qui rend range() extrêmement performant, même pour des séquences de millions d'éléments.
En Python 2, range() retournait une liste complète et xrange() était l'équivalent paresseux. En Python 3, range() se comporte comme l'ancien xrange(), et xrange() n'existe plus.
Syntaxe de range()
La fonction range() accepte un, deux ou trois arguments :
range(stop)
range(start, stop)
range(start, stop, step)Voici le détail de chaque paramètre :
| Paramètre | Description | Valeur par défaut |
|---|---|---|
start | Valeur de départ de la séquence (incluse) | 0 |
stop | Valeur de fin de la séquence (exclue) | Obligatoire |
step | Pas d'incrémentation entre chaque valeur | 1 |
Attention : la valeur stop n'est jamais incluse dans la séquence générée. C'est une source fréquente d'erreurs pour les débutants. Par exemple, range(5) génère les nombres 0, 1, 2, 3 et 4 — mais pas 5.
Exemples pratiques de range()
Utilisation avec un seul argument
Lorsque vous passez un seul argument à range(), il est interprété comme la valeur stop. La séquence commence alors à 0 :
# Générer les nombres de 0 à 4
for i in range(5):
print(i)
# Résultat :
# 0
# 1
# 2
# 3
# 4C'est la forme la plus courante de range(). Vous l'utiliserez souvent pour répéter une action un nombre précis de fois :
# Afficher "Bonjour" 3 fois
for _ in range(3):
print("Bonjour")
# Résultat :
# Bonjour
# Bonjour
# BonjourIci, la variable _ est une convention Python qui indique que la valeur de l'itération n'est pas utilisée.
Utilisation avec deux arguments
En fournissant deux arguments, vous définissez le point de départ et le point d'arrêt de la séquence :
# Générer les nombres de 2 à 7
for i in range(2, 8):
print(i)
# Résultat :
# 2
# 3
# 4
# 5
# 6
# 7Notez que nous avons passé 8 comme valeur de stop pour inclure le nombre 7 dans la séquence.
Utilisation avec trois arguments (le pas)
Le troisième argument step vous permet de contrôler l'intervalle entre chaque nombre :
# Nombres pairs de 0 à 10
for i in range(0, 11, 2):
print(i)
# Résultat :
# 0
# 2
# 4
# 6
# 8
# 10Vous pouvez également utiliser un pas négatif pour compter à rebours :
# Compte à rebours de 10 à 1
for i in range(10, 0, -1):
print(i)
# Résultat :
# 10
# 9
# 8
# 7
# 6
# 5
# 4
# 3
# 2
# 1Le pas (step) ne peut jamais être égal à zéro. Passer 0 comme pas lèvera une exception ValueError: range() arg 3 must not be zero.
Convertir un range en liste ou tuple
Même si range() est un itérable paresseux, vous pouvez facilement le convertir en list ou en tuple lorsque vous avez besoin de manipuler la séquence complète :
# Convertir en liste
nombres = list(range(1, 6))
print(nombres) # [1, 2, 3, 4, 5]
# Convertir en tuple
nombres_tuple = tuple(range(1, 6))
print(nombres_tuple) # (1, 2, 3, 4, 5)Cette technique est très utile lorsque vous devez créer rapidement une liste de nombres sans les écrire manuellement.
Utiliser range() avec enumerate()
Bien que enumerate soit souvent préféré à range() pour itérer sur une séquence existante, les deux fonctions peuvent se combiner dans certains cas :
# Utiliser range pour accéder à des indices spécifiques
fruits = ["pomme", "banane", "cerise", "datte", "figue"]
# Itérer un élément sur deux
for i in range(0, len(fruits), 2):
print(f"Index {i} : {fruits[i]}")
# Résultat :
# Index 0 : pomme
# Index 2 : cerise
# Index 4 : figueIci, nous combinons range() avec len pour accéder aux éléments d'une liste par leur index, en sautant un élément sur deux.
Utiliser range() avec des compréhensions de liste
La fonction range() est un partenaire naturel des compréhensions de liste pour créer des séquences transformées :
# Carrés des nombres de 1 à 10
carres = [x ** 2 for x in range(1, 11)]
print(carres) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# Multiples de 3 inférieurs à 30
multiples_de_3 = [x for x in range(0, 30, 3)]
print(multiples_de_3) # [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
Vérifier l'appartenance à un range
L'un des avantages de l'objet range en Python 3 est que vous pouvez tester l'appartenance d'un nombre avec l'opérateur in, et ce en temps constant (O(1)) :
# Vérifier si un nombre est dans un range
print(5 in range(10)) # True
print(15 in range(10)) # False
print(4 in range(0, 10, 2)) # True (car 4 est pair)
print(5 in range(0, 10, 2)) # False (car 5 est impair)Cette opération est extrêmement efficace car Python n'a pas besoin de parcourir toute la séquence : il calcule mathématiquement si la valeur appartient au range.
Indexation et slicing d'un range
L'objet range supporte l'indexation et le slicing, tout comme une liste :
r = range(0, 20, 3)
# Indexation
print(r[0]) # 0
print(r[3]) # 9
print(r[-1]) # 18
# Slicing (retourne un nouveau range)
print(r[2:5]) # range(6, 15, 3)
print(list(r[2:5])) # [6, 9, 12]
# Longueur
print(len(r)) # 7
Propriétés et méthodes de l'objet range
L'objet range possède trois attributs pratiques qui vous permettent d'inspecter ses propriétés :
r = range(2, 20, 3)
print(r.start) # 2
print(r.stop) # 20
print(r.step) # 3Ces attributs sont en lecture seule. Puisque l'objet range est immuable (comme un tuple ou un frozenset), vous ne pouvez pas modifier ses propriétés après sa création.
L'objet range supporte également les comparaisons d'égalité. Deux objets range sont considérés égaux s'ils génèrent la même séquence :
print(range(0, 10, 2) == range(0, 10, 2)) # True
print(range(0, 10, 2) == range(0, 12, 2)) # False
# Même séquence, représentations différentes
print(range(0) == range(2, 1, 3)) # True (les deux sont vides)
Cas d'utilisation avancés
Générer des indices inversés
Pour parcourir une liste à l'envers, range() combiné avec len est une approche classique :
couleurs = ["rouge", "vert", "bleu", "jaune"]
for i in range(len(couleurs) - 1, -1, -1):
print(couleurs[i])
# Résultat :
# jaune
# bleu
# vert
# rougePour parcourir une liste à l'envers, la fonction native reversed() est souvent plus lisible : for couleur in reversed(couleurs). Utilisez range() avec un pas négatif lorsque vous avez besoin des indices explicitement.
Créer une fonction personnalisée avec range()
Vous pouvez combiner range() avec def et yield pour créer des générateurs personnalisés :
def fibonacci_range(n):
"""Génère les n premiers nombres de Fibonacci."""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# Utilisation
for nombre in fibonacci_range(10):
print(nombre, end=" ")
# Résultat : 0 1 1 2 3 5 8 13 21 34
Utilisation avec zip()
Vous pouvez combiner range() avec zip pour créer des paires index-valeur personnalisées :
noms = ["Alice", "Bob", "Charlie"]
notes = [18, 15, 20]
resultats = {}
for i in range(len(noms)):
resultats[noms[i]] = notes[i]
print(resultats) # {'Alice': 18, 'Bob': 15, 'Charlie': 20}Dans ce cas précis, utiliser zip(noms, notes) serait plus pythonique. Cependant, range() reste utile lorsque vous avez besoin de l'indice pour d'autres opérations.
Performances et mémoire
L'un des principaux avantages de range() est son efficacité mémoire. Quelle que soit la taille de la séquence, un objet range occupe toujours la même quantité de mémoire :
import sys
# Comparer la mémoire utilisée
petit_range = range(10)
grand_range = range(10_000_000)
print(sys.getsizeof(petit_range)) # 48 octets
print(sys.getsizeof(grand_range)) # 48 octets
# Comparer avec une liste
petite_liste = list(range(10))
grande_liste = list(range(10_000_000))
print(sys.getsizeof(petite_liste)) # ~184 octets
print(sys.getsizeof(grande_liste)) # ~80 000 056 octetsComme vous pouvez le constater, l'objet range stocke uniquement trois valeurs (start, stop et step) et calcule chaque élément à la volée, tandis qu'une liste doit stocker chaque élément individuellement en mémoire.
Bonnes pratiques
Voici les bonnes pratiques à suivre lorsque vous utilisez range() en Python :
- Préférez
for item in collectionàfor i in range(len(collection)): lorsque vous n'avez pas besoin de l'index, itérez directement sur la collection. C'est plus pythonique et plus lisible. - Utilisez enumerate quand vous avez besoin de l'index ET de la valeur : plutôt que
for i in range(len(liste))suivi deliste[i], écrivezfor i, valeur in enumerate(liste). - N'appelez pas
list(range(...))inutilement : si vous n'avez besoin que d'itérer, utilisezrange()directement pour économiser de la mémoire. - Utilisez des noms de variables explicites : au lieu de
for i in range(10), préférezfor tentative in range(10)si le contexte le permet. - Pensez à
range()pour les validations : le test d'appartenance (in) sur unrangeest en O(1), c'est idéal pour vérifier si un nombre est dans un intervalle donné.
Limites de range()
La fonction range() a quelques limitations importantes à connaître :
- Entiers uniquement :
range()ne supporte que les nombres entiers. Pour des séquences de nombres à virgule flottante, vous pouvez utilisernumpy.arange()ou créer votre propre générateur. - Pas nul interdit : un pas de 0 lève une erreur
ValueError. - Séquence linéaire uniquement :
range()ne peut générer que des progressions arithmétiques (à intervalle constant).
# Erreur : range ne supporte pas les flottants
# range(0.0, 1.0, 0.1) # TypeError!
# Solution : générateur personnalisé
def frange(start, stop, step):
"""Range pour les nombres flottants."""
while start < stop:
yield round(start, 10)
start += step
for x in frange(0.0, 1.0, 0.1):
print(f"{x:.1f}", end=" ")
# Résultat : 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
Questions fréquentes
Pourquoi range() n'inclut-il pas la valeur de fin (stop) ?
C'est une convention Python inspirée de l'indexation zéro. Lorsque vous écrivez range(n), vous obtenez exactement n éléments (de 0 à n-1). Ce comportement est cohérent avec le slicing des listes et des chaînes de caractères en Python, où l'indice de fin est toujours exclu. Cela évite les erreurs de type "off by one" et rend le code plus prévisible : range(len(liste)) génère exactement les bons indices pour parcourir une list.
Quelle est la différence entre range() et list(range()) ?
range() retourne un objet range qui est un itérable paresseux occupant très peu de mémoire (environ 48 octets quelle que soit la taille). list(range()) force la création d'une list complète en mémoire, ce qui peut consommer beaucoup de ressources pour de grandes séquences. Utilisez range() directement lorsque vous itérez, et convertissez en liste uniquement si vous avez besoin de modifier la séquence ou de l'utiliser comme une liste.
Peut-on utiliser range() avec des nombres négatifs ?
Oui, absolument. Vous pouvez utiliser des valeurs négatives pour start, stop et step. Par exemple, range(-10, 0) génère les nombres de -10 à -1, et range(10, -1, -1) compte à rebours de 10 à 0. C'est particulièrement utile pour parcourir des indices en sens inverse.
Comment apprendre à bien utiliser range() et Python en général ?
Pour maîtriser range() et l'ensemble de l'écosystème Python, la pratique régulière est essentielle. Nous vous recommandons de suivre notre formation dédiée à Python sur Believemy qui vous guidera pas à pas, depuis les bases comme range() jusqu'aux concepts avancés comme les générateurs et les dataclass. Vous y trouverez des exercices pratiques et des projets concrets pour consolider vos connaissances.