IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Indépendance des listes d'une liste


Sujet :

Python

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut Indépendance des listes d'une liste
    Bonjour,

    Je cherche à comprendre l'affichage produit par ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    def image_print(image):
    	for line in image:
    		print line
     
    # Partie 1
    distance = [0]*10 
    distance[1] = 7
    image_print(distance)
     
    # Partie 2
    distance = [[0]*10]*10
    distance[0][2] = 6
    distance[1][2] = 1
    image_print(distance)
    Soit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    0
    7
    0
    0
    0
    0
    0
    0
    0
    0
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    Dans la partie 1, les éléments de la liste semble indépendants : modifier un élément ne modifie pas les autres.

    Dans la partie 2, les éléments semblent dépendants.

    Je cherche à comprendre pourquoi et surtout comment construire correctement ma liste de listes pour m'en servir comme une matrice.

    Merci d'avance pour vos lumières

  2. #2
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 997
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 997
    Par défaut
    Tu trouveras sans doute ta réponse ici

  3. #3
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    Bonjour

    Dans la partie 2, [0]*10 est une liste qui est évaluée une seule fois.
    [ [0]*10 ]*10 repète 10 fois la même liste, le tout étant mis dans une liste.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    >>> distance = [ [0]*10 ] * 10
    >>> for i in range(10):
    ...     print id(distance[i])
    ... 
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    3077631788
    >>>
    Pour une "vraie" "matrice" 10x10 remplie de 0, je ferais comme ça

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    distance = map(lambda i:[0]*10, range(10))

  4. #4
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Citation Envoyé par plxpy Voir le message
    Pour une "vraie" "matrice" 10x10 remplie de 0, je ferais comme ça

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    distance = map(lambda i:[0]*10, range(10))
    Il y a plus simple, utiliser une liste comprehension*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    distance = [[0] * 10 for i in range(10)]

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur en formation
    Inscrit en
    Juillet 2013
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur en formation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2013
    Messages : 300
    Par défaut
    Cela semble être une question de portée des variables. Dans la partie 1, nous avons une liste d'entiers, donc de simples valeurs, en effet, voici un code qui te montrera la différence entre les int et les listes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    a=32
    b=a
    a*=6
    print(b)
    >>> 32 #si l'on change a ou b, l'autre variable n'est pas modifiée, ces 2 variables sont de simples valeurs
    #alors que :
    a=[45,"rien"]
    b=a
    b.append("ajout")
    print(a)
    >>> [45,'rien','ajout'] #là, a et b sont 2 variables qui pointent sur le même objet, par conséquent, si l'un est modifié, l'autre aussi
    # la solution est de copier explicitement de contenu de a dans b pour avoir des objets indépendants :
    b=list(a)
    Pour en revenir à notre problème, dans la partie 1, nous avons une liste de 'int', donc des valeurs pures. La modification de l'un ne modifie pas les autres.
    En revanche, dans la partie 2, on modifie une liste, c'est la même liste qui est contenue 10 fois dans notre liste de listes. En modifiant l'une des listes qui est en fait un objet, on modifie les autres qui pointent sur le même objet.
    La solution est donc d'exprimer explicitement la différence entre les listes, qu'elles aient le même contenu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ligne=[0]*10
    distance=[]
    for i in range(10) : distance.append(list(ligne))
    ps : je suis encore débutant, vous pouvez m'expliquer le mot clé lambda, je n'ai pas réussi à trouver de tuto avec

  6. #6
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    lambda est le mot clé pour créer des “fonctions anonymes” en python…

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 491
    Billets dans le blog
    1
    Par défaut
    Merci pour les réponses.

    La manière de faire est aussi dans la FAQ de Developpez, je l'ai trouvé après l'avoir posté : http://python.developpez.com/faq/?page=Liste#listlist

    J'avais bien remarqué / compris que la partie 2 mène à une duplication de la référence de la liste et non à la duplication de la liste elle-même. Même référence, on voit bien ce que ça va donner.

    En revanche, je ne suis pas vraiment d'accord avec l'analyse de stalacta : dans la partie 1, on duplique bien une liste et non un int et c'est liste est bien dupliquée, ce n'est pas que sa référence :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> distance = [0]
    >>> print distance
    [0]
    >>> type(distance)
    <type 'list'>
    >>> distance = distance * 10
    >>> distance
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    >>> distance [1] = 42
    >>> distance
    [0, 42, 0, 0, 0, 0, 0, 0, 0,
    Pour mes tests, j'ai fait ça (à la suite du code précédent dans l'interpréteur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> distance = distance * 10
    >>> distance
    [0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0]
    >>> distance = [0]
    >>> distance = distance * 10
    >>> print distance
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    >>> distance = [distance] * 10
    >>> print distance
    Ça dépend donc du type du contenu de la liste. Les types primitifs sont dupliqués, les types non primitifs (donc des objets* je présume ?) ne le sont pas : leur référence l'est. Je pense que je présume bien car une liste a des méthodes, donc c'est un objet. Ou alors Python fait vraiment des trucs bizarres

    * : Je crois avoir lu un truc sur les objets immutables mais je retrouve plus où. Java fait aussi des choses dans le même genre : types primitifs + objets immutable VS autres objets.

    Bref, comportement qui me surprend mais qui s'explique. Merci

  8. #8
    Membre éclairé
    Homme Profil pro
    Développeur en formation
    Inscrit en
    Juillet 2013
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur en formation
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2013
    Messages : 300
    Par défaut
    Citation Envoyé par Bktero Voir le message
    En revanche, je ne suis pas vraiment d'accord avec l'analyse de stalacta : dans la partie 1, on duplique bien une liste et non un int et c'est liste est bien dupliquée, ce n'est pas que sa référence :
    Non, dans la partie 1, c'est en réalité les 2 à la fois, lorsque tu fais [0]*10, python appelle la méthode __mul__(il me semble que c'est ça) de la classe list, qui renvoie une liste de 10 fois le contenu (donc également les références mais nous travaillons encore une fois avec des int qui ne respectent pas les règles "normales") de la liste passée en paramètre (là [0]). Si le 0 est entre crochets, c'est pour appeler la méthode __mul__ de la classe list au lieu de la classe int, mais dans l'absolu, ce serait tout à fait envisageable, la preuve que ce ne sont pas des liste qui sont copiées dans la première partie, si c'était la liste qui était dupliquée, on aurait : [[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]].
    Les crochets sont faits pour appeler la méthode __mul__ de la bonne classe.
    Pour mieux comprendre, voici la manière la plus explicite de l'exprimer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    list.__mul__([0],10) # et là, cette méthode prend obligatoirement
    #une liste en premier paramètre puisque celle-ci ira dans le traditionnel 'self'
    #même en étant appelée explicitement, ce que je veux te faire comprendre,
    #c'est que ce n'est pas la liste qui est copiée mais son contenu
    En tout cas, même si l'explication n'est pas super simple à comprendre, la solution marche.

    ps : merci, je vais me taper tout à traduire mais merci pour lambda (enfin je me suis bien débrouillé sans jusqu'à maintenant mais bon...)

  9. #9
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    En python, tout est objet, y compris les types de bases comme int ou float.

    Par ailleurs, les copies/assignations/etc. ont toujours lieu par référence. La grande différence entre un int et une liste, c’est que le premier est immutable, c’est-à-dire qu’il ne peut pas être modifié. Dans*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    l = [0] * 10
    l[1] = 20
    …On ne modifie pas l’objet contenu dans l[1] (ce n’est par définition pas possible), on assigne à l[1] un nouvel objet int. Alors que dans*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    l = [[0] * 10] * 10
    l[1][1] = 20
    …On modifie l’objet contenu dans l[1] (une liste, qui est mutable), qui se trouve être également copié par référence dans tous les autres éléments de l.

    On peut aussi essayer avec une liste de tuples (ces derniers étant aussi imutable)*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    l = [(0,) * 10] * 10
    # l[1][1] = 20  # ERREUR: TypeError: 'tuple' object does not support item assignment
    l[1] = (0, 20, 0, 0, 0, 0, 0, 0, 0, 0)
    On ne peut pas modifier un tuple, il faut ré-assigner un nouveau tuple à l’élément de l voulu, et là pas de problème, seul l[1][1] vaudra 20*!

    PS*: On peut utiliser id() pour vérifier que l contient bien 10 fois le même objet à chaque fois*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    print([id(e) for e in [0] * 10])
    print([id(e) for e in [[0] * 10] * 10])
    print([id(e) for e in [(0,) * 10] * 10])

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 771
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Bktero Voir le message
    * : Je crois avoir lu un truc sur les objets immutables mais je retrouve plus où. Java fait aussi des choses dans le même genre : types primitifs + objets immutable VS autres objets.
    Salut
    Très bien expliqué ici (et avec beaucoup d'humour)...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  11. #11
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 591
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 591
    Par défaut
    Salut,
    Ah les mutables...
    En fait ici, ils ne sont pas concernés.
    Soit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >>> class A: pass
    ...
    >>> row=[ A() for _ in range(3) ]
    >>> row
    [<__main__.A instance at 0x01FCDC88>, <__main__.A instance at 0x01FCDC60>, <_main__.A instance at 0x01FCDCB0>]
    Fabriquons l'array par "copy"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    >>> array=[row]*3
    >>> for _ in range(3): print array[_]
    ...
    [<__main__.A instance at 0x01FCDC88>, <__main__.A instance at 0x01FCDC60>, <__main__.A instance at 0x01FCDCB0>]
    [<__main__.A instance at 0x01FCDC88>, <__main__.A instance at 0x01FCDC60>, <__main__.A instance at 0x01FCDCB0>]
    [<__main__.A instance at 0x01FCDC88>, <__main__.A instance at 0x01FCDC60>, <__main__.A instance at 0x01FCDCB0>]
    array est une liste dont les index [0-2] sont des références au même objet row. modifier array[1][1] équivaut a modifier row[1] et par construction array[0] et array[2].
    Dit autrement, modifier array[1][1] c'est changer ce qui a été assigne en [1][1] - par un autre objet (mutable ou pas) -.
    De toutes façons, changer l’état de l'objet assigne en [1][1] passe par l'appel d'une de ses méthodes - c'est a ce moment que mutable ou pas intervient.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre Expert Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Par défaut
    Pour info et pour les curieux, voici la fonction qui, dans l'interpréteur, réalise la multiplication d'une liste (Python-2.7.5/Objects/listobject.c, lignes 544 à 582).

    Ce sont bien des adresses de PyObject qui sont manipulées/(re)copiées.

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    static PyObject *
    list_repeat(PyListObject *a, Py_ssize_t n)
    {
        Py_ssize_t i, j;
        Py_ssize_t size;
        PyListObject *np;
        PyObject **p, **items;
        PyObject *elem;
        if (n < 0)
            n = 0;
        if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)
            return PyErr_NoMemory();
        size = Py_SIZE(a) * n;
        if (size == 0)
            return PyList_New(0);
        np = (PyListObject *) PyList_New(size);
        if (np == NULL)
            return NULL;
     
        items = np->ob_item;
        if (Py_SIZE(a) == 1) {
            elem = a->ob_item[0];
            for (i = 0; i < n; i++) {
                items[i] = elem;
                Py_INCREF(elem);
            }
            return (PyObject *) np;
        }
        p = np->ob_item;
        items = a->ob_item;
        for (i = 0; i < n; i++) {
            for (j = 0; j < Py_SIZE(a); j++) {
                *p = items[j];
                Py_INCREF(*p);
                p++;
            }
        }
        return (PyObject *) np;
    }

  13. #13
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 591
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 591
    Par défaut
    Salut,

    Citation Envoyé par plxpy Voir le message
    Ce sont bien des adresses de PyObject qui sont manipulées/(re)copiées.
    De toutes façons des qu'un objet (liste ou autre) contient d'autres objets, le dupliquer signifie créer un objet de même type contenant des références aux mêmes objets ou de copies de ces objets (de nouvelles instances).
    Et a partir de la, la question nouvelle instance ou référence devient récursive: on crée ou pas une nouvelle arborescence d'objets.

    Voir les opérations de copy dans la documentation de Python (qui couvre des questions qui ne sont pas spécifiques a Python mais a la structure arborescente des objets composites).

    La ou le débutant se fait "avoir", c'est en choisissant une /list/ pour y stocker des types simples comme int, float,...
    Une /list/ d'objets de type int ressemble a un tableau d'entiers mais /array/ serait une "boite" plus adaptée pour représenter une suite de valeurs de type simple - entiers, float -.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Lisp][IA] Supprimer une liste d'une liste de listes
    Par Superleo2999 dans le forum Lisp
    Réponses: 5
    Dernier message: 22/03/2010, 11h51
  2. Passer des paramétres d'une liste a une autre Liste
    Par can48yous dans le forum Composants
    Réponses: 4
    Dernier message: 10/06/2008, 14h42
  3. Appel d'une liste dans une liste (JSTL)
    Par abalgue dans le forum Hibernate
    Réponses: 4
    Dernier message: 15/06/2007, 11h56
  4. STL : retirer une liste d'une liste
    Par DEVfan dans le forum SL & STL
    Réponses: 13
    Dernier message: 05/01/2007, 21h49
  5. Réponses: 4
    Dernier message: 30/08/2006, 13h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo