Comprendre les pièges courants des listes en Python lors des entretiens techniques

Lors des entretiens techniques en Python, la manipulation des listes et la gestion de la mémoire sont souvent évaluées à travers des exercices piégés. Voici une analyse détaillée de quatre cas classiques qui mettent en lumière le comportement des objets mutables et la portée des variables.

Cas 1 : Modification d'une liste pendant son itération

Considérez le scénario suivant où l'on tente de supprimer des éléments d'une liste tout en la parcourant :


nombres = [10, 20, 30, 40, 50]
for nb in nombres:
    nombres.remove(nb)
print(nombres)

Résultat : [20, 40]

Beaucoup s'attendraient à ce que la liste soit vide à la fin. Cependant, il faut se rappeler deux principes fondamentaux :

  1. Les listes sont des séquences mutables ; leur taille change lors de la suppression d'éléments.
  2. La boucle for en Python utilise un index interne pour itérer.

Explication étape par étape :

  • Itération 1 : Index 0. nb vaut 10. Après suppression, la liste devient [20, 30, 40, 50].
  • Itération 2 : Index 1. L'élément à l'index 1 est maintenant 30 (et non 20). Après suppression de 30, la liste devient [20, 40, 50].
  • Itération 3 : Index 2. L'élément à l'index 2 est 50. Après suppression, la liste devient [20, 40].
  • L'itération s'arrête car la longueur de la liste (2) est atteinte. Les éléments 20 et 40 sont ignorés.

Cas 2 : Portée des variables et mutabilité

Ce problème teste la compréhension des variables locales, globales et de la mutabilité des objets :


compteur = 5
elements = []

def modifier_donnees():
    compteur = 10
    elements.append('nouveau')

modifier_donnees()
print(compteur, elements)

Résultat : 5 ['nouveau']

Deux concepts sont évalués ici :

  1. Portée des variables : compteur est un entier (immutable). L'assignation compteur = 10 dans la fonction crée une variable locale, laissant la variable globale inchangée.
  2. Mutabilité des listes : elements est une liste (mutable). La fonction ne réassigne pas la variable, mais modifie l'objet en mémoire pointé par la variable globale. L'ajout d'un élément affecte donc la liste globale.

Cas 3 : Arguments par défaut mutables dans les fonctions

L'utilisation d'une liste comme valeur par défaut est un piège classique :


def ajouter_element(item, collection=[]):
    collection.append(item)
    return collection

res1 = ajouter_element('alpha')
res2 = ajouter_element('beta', [])
res3 = ajouter_element('gamma')
print(res1, res2, res3)

Résultat : ['alpha', 'gamma'] ['beta'] ['alpha', 'gamma']

Explication : En Python, les arguments par défaut sont évalués une seule fois, lors de la définition de la fonction. L'objet liste collection=[] est créé en mémoire et partagé entre tous les appels qui n'explicitent pas cet argument. Ainsi, res1 et res3 pointent vers la même liste, qui accumule les valeurs. Le deuxième appel fournit sa propre liste vide, isolant le résultat.

Cas 4 : Arguments par défaut mutables dans les méthodes de classe

Le même piège s'applique aux méthodes d'une classe :


class Processeur:
    def traiter_donnees(self, donnees=[]):
        donnees.sort()
        donnees.append('fin')
        return donnees

inst1 = Processeur()
print(inst1.traiter_donnees())
inst2 = Processeur()
print(inst2.traiter_donnees())
inst3 = Processeur()
print(inst3.traiter_donnees(donnees=['z', 'a']))
inst4 = Processeur()
print(inst4.traiter_donnees())

Résultat :


['fin']
['fin', 'fin']
['a', 'z', 'fin']
['fin', 'fin', 'fin']

La liste par défaut donnees=[] est liée à la méthode au moment de la définition de la classe, et non à l'instance. Par conséquent, toutes les instancse qui n'utilisent pas d'argument explicite partagent et modifient la même liste sous-jacente, ce qui entraîne une accumulation des éléments à chaque appel.

Étiquettes: Python mutable-default-arguments variable-scope list-iteration object-mutability

Publié le 12 septembre à 18h51