317 votes

Concaténation de deux listes - différence entre '+=' et extend()

J'ai vu qu'il y avait en fait deux façons (peut-être plus) de concaténer des listes en Python :

L'une des solutions consiste à utiliser la fonction extend() méthode :

a = [1, 2]
b = [2, 3]
b.extend(a)

l'autre pour utiliser l'opérateur plus (+) :

b += a

Maintenant je me demande : laquelle de ces deux options est la façon "pythonique" de faire de la concaténation de listes et y a-t-il une différence entre les deux ? (J'ai consulté le tutoriel officiel de Python mais je n'ai rien trouvé à ce sujet).

1 votes

La différence a peut-être plus d'implications lorsqu'il s'agit de faire du ducktyping et si votre peut-être pas vraiment une liste mais comme une liste soutiens .__iadd__() / .__add__() / .__radd__() par rapport à .extend()

6voto

grofte Points 618

La méthode .extend() sur les listes fonctionne avec n'importe quel itérable*, += fonctionne avec certains mais peut devenir bizarre.

import numpy as np

l = [2, 3, 4]
t = (5, 6, 7)
l += t
l
[2, 3, 4, 5, 6, 7]

l = [2, 3, 4]
t = np.array((5, 6, 7))
l += t
l
array([ 7,  9, 11])

l = [2, 3, 4]
t = np.array((5, 6, 7))
l.extend(t)
l
[2, 3, 4, 5, 6, 7]

Python 3.6
*presque sûr que .extend() fonctionne avec n'importe quel itérable mais merci de commenter si c'est incorrect

Modification : "extend()" devient "La méthode .extend() sur les listes". Note : Le commentaire de David M. Helmuth ci-dessous est très clair.

5voto

VicX Points 694

Desde el Code source de CPython 3.5.2 : Pas de grande différence.

static PyObject *
list_inplace_concat(PyListObject *self, PyObject *other)
{
    PyObject *result;

    result = listextend(self, other);
    if (result == NULL)
        return result;
    Py_DECREF(result);
    Py_INCREF(self);
    return (PyObject *)self;
}

5voto

dalonsoa Points 95

En fait, il existe des différences entre les trois options : ADD , INPLACE_ADD y extend . Le premier est toujours plus lent, tandis que les deux autres sont à peu près identiques.

Sur la base de ces informations, je préférerais utiliser extend qui est plus rapide que ADD et me semble plus explicite sur ce que vous êtes en train de faire que INPLACE_ADD .

Essayez le code suivant plusieurs fois (pour Python 3) :

import time

def test():
    x = list(range(10000000))
    y = list(range(10000000))
    z = list(range(10000000))

    # INPLACE_ADD
    t0 = time.process_time()
    z += x
    t_inplace_add = time.process_time() - t0

    # ADD
    t0 = time.process_time()
    w = x + y
    t_add = time.process_time() - t0

    # Extend
    t0 = time.process_time()
    x.extend(y)
    t_extend = time.process_time() - t0

    print('ADD {} s'.format(t_add))
    print('INPLACE_ADD {} s'.format(t_inplace_add))
    print('extend {} s'.format(t_extend))
    print()

for i in range(10):
    test()

ADD 0.3540440000000018 s
INPLACE_ADD 0.10896000000000328 s
extend 0.08370399999999734 s

ADD 0.2024550000000005 s
INPLACE_ADD 0.0972940000000051 s
extend 0.09610200000000191 s

ADD 0.1680199999999985 s
INPLACE_ADD 0.08162199999999586 s
extend 0.0815160000000077 s

ADD 0.16708400000000267 s
INPLACE_ADD 0.0797719999999913 s
extend 0.0801490000000058 s

ADD 0.1681250000000034 s
INPLACE_ADD 0.08324399999999343 s
extend 0.08062700000000689 s

ADD 0.1707760000000036 s
INPLACE_ADD 0.08071900000000198 s
extend 0.09226200000000517 s

ADD 0.1668420000000026 s
INPLACE_ADD 0.08047300000001201 s
extend 0.0848089999999928 s

ADD 0.16659500000000094 s
INPLACE_ADD 0.08019399999999166 s
extend 0.07981599999999389 s

ADD 0.1710910000000041 s
INPLACE_ADD 0.0783479999999912 s
extend 0.07987599999999873 s

ADD 0.16435900000000458 s
INPLACE_ADD 0.08131200000001115 s
extend 0.0818660000000051 s

5voto

dniq Points 1

Ary += ext crée un nouvel objet Liste, puis y copie les données des listes "ary" et "ext".

ary.extend(ext) ajoute simplement une référence à la liste "ext" à la fin de la liste "ary", ce qui réduit le nombre de transactions en mémoire.

Par conséquent, .extend fonctionne beaucoup plus rapidement et n'utilise pas de mémoire supplémentaire en dehors de la liste en cours d'extension et de la liste avec laquelle elle est étendue.

╰─➤ time ./list_plus.py
./list_plus.py  36.03s user 6.39s system 99% cpu 42.558 total
╰─➤ time ./list_extend.py
./list_extend.py  0.03s user 0.01s system 92% cpu 0.040 total

Le premier script utilise également plus de 200 Mo de mémoire, tandis que le second n'utilise pas plus de mémoire qu'un processus python3 "nu".

Cela dit, l'ajout sur place semble faire la même chose que l'extension.

3voto

Flux Points 729

J'ai consulté le tutoriel officiel de Python, mais je n'ai rien trouvé à ce sujet.

Il se trouve que cette information est enfouie dans le FAQ sur la programmation :

... pour les listes, __iadd__ [i.e. += est équivalent à l'appel de extend sur la liste et de renvoyer la liste. C'est pourquoi nous disons cela pour les listes, += est une abréviation de list.extend

Vous pouvez également le constater par vous-même dans le code source de CPython : https://github.com/python/cpython/blob/v3.8.2/Objects/listobject.c#L1000-L1011

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X