ProgrammationDéveloppeur Backend

Décrivez le mécanisme de travail des compréhensions de listes en Python. En quoi la compréhension de listes se distingue-t-elle de la fonction map() et des boucles for, quels sont les avantages et les inconvénients de chaque approche et quelles erreurs peuvent survenir en cas d'application maladroite ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Les compréhensions de listes sont un moyen concis de créer des listes à partir d'objets itérables existants à l'aide d'une syntaxe courte :

squares = [x**2 for x in range(10)]

Cette expression est équivalente à :

squares = [] for x in range(10): squares.append(x**2)

Les compréhensions de listes ont plusieurs avantages :

  • Brevité et lisibilité (surtout pour des transformations simples) ;
  • Possibilité d'inclure des conditions (filtre) : evens = [x for x in range(10) if x % 2 == 0] ;
  • Les expressions renvoient immédiatement une liste, leur résultat peut être utilisé ultérieurement.

L'analogie avec map() :

def f(x): return x**2 squares = list(map(f, range(10)))

map est plus rapide sur de grandes données, s'il utilise une fonction déjà existante en C, et convient pour appliquer une fonction à tous les éléments. La compréhension de listes est destinée à des expressions, pas uniquement à des fonctions prêtes à l'emploi. La boucle for est plus flexible, mais plus encombrante.


Question piège.

Pourquoi, dans une expression telle que [x for x in range(10)], la variable x se retrouve-t-elle accessible en dehors de l'expression en Python2, mais pas en Python3 ?

Réponse : En Python2, la variable de boucle (x) conserve sa valeur après l'exécution de la compréhension de listes. En Python3, elle est "isolée" et n'est pas accessible en dehors de la liste, ce qui empêche des effets secondaires indésirables.

Exemple :

# Python 2.x: [x for x in range(3)] print(x) # x == 2 # Python 3.x: [x for x in range(3)] print(x) # NameError: name 'x' is not defined

Exemples d'erreurs réelles dues à la méconnaissance des subtilités du sujet.


Histoire 1

Un développeur sur un grand projet voulait filtrer et créer une nouvelle liste via une compréhension de listes :

my_list = [item.transform() for item in data if item.is_valid()]

Mais l'opération item.transform() déclenchait une erreur lorsque item.is_valid() retournait False. Pourtant, la fonction de vérification était écrite avec un effet secondaire potentiel, ce qui a finalement brisé certaines parties du code avec des effets secondaires de manière non évidente.


Histoire 2

Dans le cadre d'une migration de Python2 à Python3, un développeur était convaincu que la variable de boucle resterait accessible :

[x for x in range(5)] print(x) # S'attendait à obtenir 4, mais a reçu NameError.

Cela a causé un bogue dans la logique cyclique, où la variable devait rester nécessaire en dehors de la compréhension.


Histoire 3

Utilisation de compréhensions de listes imbriquées sans indiquer explicitement les niveaux :

def flatten(matrix): return [cell for row in matrix for cell in row]

Les débutants rencontrent souvent des erreurs à cause d'un ordre d'itération incorrect (par exemple, [cell for cell in row for row in matrix] ou d'un enchaînement excessif), ce qui conduit à un résultat incorrect — une liste unidimensionnelle au lieu de bidimensionnelle ou inversement.