Aller au contenu

Activité - Problèmes de copie

On rappelle qu'en Python les tableaux (type list) sont muables : on peut changer un élément du tableau sans changer d'objet. Par conséquent, l'affectation « = » ne construit pas un nouvel objet mais lui attribue une deuxième étiquette (qui affecte le même objet).

Pour vous en convaincre, étudiez de nouveau cette page du chapitre sur la Portée des variables.

Les paragraphes de cette page vont vous présenter différentes situations qui ont pour but de remettre en cause votre instinct et nécessiteront de la réflexion de votre part à chaque fois que vous voudrez copier des tableaux (et encore plus des tableaux de tableaux).

Act B05.11 - Sous-tableaux distincts ?

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
>>> matrice = []

>>> tab = [0]*5

>>> tab


>>> for i in range(3):
        matrice.append(tab)

>>> matrice


>>> matrice[0][1] = 7

>>> matrice

Anticipez les résultats des instructions des lignes 5, 11 et 17 avant de les tester dans la console.

Réponses et compléments
  1. Après les deux premières instructions :

    1
    2
    3
    >>> matrice = []
    
    >>> tab = [0]*5
    

    on est dans la situation : Ainsi, l'instruction de la ligne 5 donne :

    5
    6
    >>> tab
    [0, 0, 0, 0, 0]
    

  2. L'instruction suivante :

    8
    9
    >>> for i in range(3):
            matrice.append(tab)
    

    Ajoute trois éléments à matrice qui pointent tous vers le tableau tab : L'instruction de la ligne 11 donne l'illusion que tout va bien :

    >>> matrice linenums="11"
    [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
    

  3. Les instructions des lignes 14 et 16

    14
    15
    16
    17
    >>> matrice[0][1] = 7
    
    >>> matrice
    [[0, 7, 0, 0, 0], [0, 7, 0, 0, 0], [0, 7, 0, 0, 0]]
    
    montrent que l'on modifie simultanément le deuxième élément des trois lignes, ce qui n'est certainement pas l'effet désiré :

Act B05.12 - Nouveau tableau ou nouvelle étiquette ?

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
>>> A = [[1,2],[3,4]]

>>> B = [[1,2],[3,4]]

>>> C = A

>>> A == B


>>> A is B


>>> C == A


>>> A is C
  1. Rappelez la signification du test « == » et du test « is ».

    Réponse du 1.

    Un test qui utilise « == » renvoie True lorsque les deux objets comparés ont les mêmes contenus.

    Un test qui utilise « is » renvoie True lorsque les deux objets comparés sont les mêmes objets en mémoire.

  2. Anticipez les résultats des instructions des lignes 7, 10, 13 et 16.

    Réponse du 2.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> A = [[1,2],[3,4]]
    
    >>> B = [[1,2],[3,4]]
    
    >>> C = A
    
    >>> A == B
    True
    
    >>> A is B
    False
    
    >>> C == A
    True
    
    >>> A is C
    True
    

    schéma mémoire

Act B05.13 - Nouvelle étiquette et modification d'éléments

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> A = [[1,2],[3,4]]

>>> B = A

>>> B[1] = [42, 666]

>>> A


>>> B

Anticipez les valeurs renvoyées par les instructions des lignes 7 et 10 avant de tester ces instructions dans la console et de consulter le complément d'explication ci-dessous.

Une réponse
  1. Après les deux premières instructions :

    1
    2
    3
    >>> A = [[1,2],[3,4]]
    
    >>> B = A
    

    on est dans la situation : Toute modification de B entraîne donc une modification de A.

  2. L'instruction suivante :

    5
    >>> B[1] = [42, 666]
    

    redéfinit B[1], donc redéfinit A[1] :

  3. On peut le constater en testant ces instructions dans la console :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    >>> A = [[1,2],[3,4]]
    
    >>> B = A
    
    >>> B[1] = [42, 666]
    
    >>> A
    [[1, 2], [42, 666]]
    
    >>> B
    [[1, 2], [42, 666]]
    

Act B05.14 - Nouveau tableau mais mêmes sous-tableaux

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> A = [[1,2],[3,4]]

>>> B = [[], []]

>>> B[0] = A[0]

>>> A[1] = B[1]

>>> A


>>> B


>>> B == A


>>> B[0] == A[0]


>>> B[1] == A[1]


>>> B is A


>>> B[0] is A[0]


>>> B[1] is A[1]

Anticipez les résultats des instructions des lignes 9, 12, 15, 18, 21, 24, 27 et 30 avant de tester ces instructions dans la console.

Réponses et compléments
  1. Après les deux premières instructions :

    1
    2
    3
    >>> A = [[1,2],[3,4]]
    
    >>> B = [[], []]
    

    on est dans la situation :

  2. Après les deux instructions suivantes :

    5
    6
    7
    >>> B[0] = A[0]
    
    >>> A[1] = B[1]
    

    on est dans la situation :

  3. Ces shémas permettent de comprendre les résultats des instructions des lignes 9, 12, 15, 18, 21, 24, 27 et 30 :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    >>> A = [[1,2],[3,4]]
    
    >>> B = [[], []]
    
    >>> B[0] = A[0]
    
    >>> A[1] = B[1]
    
    >>> A
    [[1, 2], []]
    
    >>> B
    [[1, 2], []]
    
    >>> B == A
    True
    
    >>> B[0] == A[0]
    True
    
    >>> B[1] == A[1]
    True
    
    >>> B is A
    False
    
    >>> B[0] is A[0]
    True
    
    >>> B[1] is A[1]
    True
    
    On peut noter que les objets A et B sont distincts, mais que leurs tableaux internes peuvent tout de même être le même objet.

Act B05.15 - Un même sous-tableau, pas l'autre

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> A = [[1,2],[3,4]]

>>> B = [[], []]

>>> B[0] = A[0]

>>> B[1] = A[1]

>>> B[0] = [666, 42]

>>> B[1][0] = 69


>>> A


>>> B
  1. Anticipez les résultats des instructions des lignes 14 et 17 avant de tester ces instructions dans la console.

    Réponses du 1.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    >>> A = [[1,2],[3,4]]
    
    >>> B = [[], []]
    
    >>> B[0] = A[0]
    
    >>> B[1] = A[1]
    
    >>> B[0] = [666, 42]
    
    >>> B[1][0] = 69
    
    
    >>> A
    [[1, 2], [69, 4]]
    
    >>> B
    [[666, 42], [69, 4]]
    
  2. Expliquez les résultats obtenus.

    Réponses du 2. et compléments

    On a vu, avec le paragraphe précédent que B n'est pas A (puisque B est obtenu à l'aide d'une affectation d'un nouvel objet de type list). Ce qu'il faut ensuite comprendre est pourquoi :

    • la modification de B[0] n'affecte pas A ;
    • tandis que la modification de B[1][0] affecte A.

    Suivons les instrutions effectuées les unes après les autres.

    1. Après les deux premières instructions :

      1
      2
      3
      >>> A = [[1,2],[3,4]]
      
      >>> B = [[], []]
      
      on est dans la situation :

    2. Après les deux instructions suivantes :

      5
      6
      7
      >>> B[0] = A[0]
      
      >>> A[1] = B[1]
      
      la situation devient :
      c'est à dire:

    3. On effectue ensuite l'instruction :

      9
      >>> B[0] = [666, 42]
      
      B[0] est modifié par affectation d'un nouveau tableau, c'est-à-dire par :
      - création d'un nouvel objet de type list : [666, 42] ;
      - affectation de ce nouveau tableau à B[0].
      Puisque B is not A, le tableau étiqueté par A n'est pas affecté.
      La situation est alors la suivante :

    4. Ensuite, B[1][0] est modifié par affectation d'un nouvel objet.
      Mais B[1] is A[1], c'est-à-dire les étiquettes B[1] et A[1] correspondent au même objet, donc cette affectation concerne également A[1]. Ainsi, après la ligne :

      11
      >>> B[1][0] = 69
      
      la situation est la suivante :

Act B05.16 - Nouveau tableau ou nouvelle étiquette ? (bis)

On considère les instructions suivantes, saisies en console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> A = [[1,2],[3,4]]

>>> B = A

>>> B[0] = [666, 42]

>>> B[1][0] = 69

>>> A


>>> B
  1. Anticipez les résultats des instructions des lignes 10 et 13 avant de tester ces instructions dans la console.

    Réponses du 1.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    >>> A = [[1,2],[3,4]]
    
    >>> B = A
    
    >>> B[0] = [666, 42]
    
    >>> B[1][0] = 69
    
    >>> A
    [[666, 42], [69, 4]]
    
    >>> B
    [[666, 42], [69, 4]]
    
  2. Expliquez les résultats obtenus.

    Réponses du 2. et compléments

    La différence avec le paragraphe précédent tient dans l'affectation initiale de B : B = A.
    A cause de cette affectation, B est une étiquette sur l'objet A donc toute modification des contenus de B est une modification de A (et vice-versa).

    1. Après les deux premières instructions :

      1
      2
      3
      >>> A = [[1,2],[3,4]]
      
      >>> B = A
      
      on est dans la situation :

    2. Après l'instruction suivante :

      5
      >>> B[0] = [666, 42]
      
      la situation devient :

    3. Après l'instruction suivante :

      7
      >>> B[1][0] = 69
      
      la situation devient :