Jaka jest różnica między „.append ()” a „+ = []” w Pythonie?

Odpowiedzi:

161

W twoim przypadku jedyną różnicą jest wydajność: dołączanie jest dwa razy szybsze.

Python 3.0 (r30:67507, Dec  3 2008, 20:14:27) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> timeit.Timer('s.append("something")', 's = []').timeit()
0.20177424499999999
>>> timeit.Timer('s += ["something"]', 's = []').timeit()
0.41192320500000079

Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> timeit.Timer('s.append("something")', 's = []').timeit()
0.23079359499999999
>>> timeit.Timer('s += ["something"]', 's = []').timeit()
0.44208112500000141

W ogólnym przypadku appenddoda jedną pozycję do listy, podczas gdy wszystkie elementy listy po prawej stronie +=zostaną skopiowane do listy po lewej stronie.

Aktualizacja: analiza perf

Porównując bajtkody możemy założyć, że appendwersja marnuje cykle na LOAD_ATTR+ CALL_FUNCTION, a + = wersja - na BUILD_LIST. Najwyraźniej BUILD_LISTprzeważa LOAD_ATTR+ CALL_FUNCTION.

>>> import dis
>>> dis.dis(compile("s = []; s.append('spam')", '', 'exec'))
  1           0 BUILD_LIST               0
              3 STORE_NAME               0 (s)
              6 LOAD_NAME                0 (s)
              9 LOAD_ATTR                1 (append)
             12 LOAD_CONST               0 ('spam')
             15 CALL_FUNCTION            1
             18 POP_TOP
             19 LOAD_CONST               1 (None)
             22 RETURN_VALUE
>>> dis.dis(compile("s = []; s += ['spam']", '', 'exec'))
  1           0 BUILD_LIST               0
              3 STORE_NAME               0 (s)
              6 LOAD_NAME                0 (s)
              9 LOAD_CONST               0 ('spam')
             12 BUILD_LIST               1
             15 INPLACE_ADD
             16 STORE_NAME               0 (s)
             19 LOAD_CONST               1 (None)
             22 RETURN_VALUE

Możemy jeszcze bardziej poprawić wydajność, usuwając LOAD_ATTRnarzut:

>>> timeit.Timer('a("something")', 's = []; a = s.append').timeit()
0.15924410999923566
Constantin
źródło
12
+1: To jest bardzo interesujące. I tak używam append, ponieważ daje to jaśniejszy kod. Ale nie zdawałem sobie sprawy, że jest różnica w wydajności. Jeśli już, spodziewałbym się, że append będzie wolniejszy, ponieważ jest to gwarantowane wywołanie funkcji, podczas gdy zakładałem, że + = będzie dalej optymalizowane.
DNS
2
Czy nie ma też różnicy funkcjonalnej? Na przykład niech a = [] , b = [4,5,6] , tutaj jeśli zrobisz c = a.append (b) to c będzie listą listy [[4,5,6]], podczas gdy c + = b ; prowadziłoby do prostej listy c = [4,5,6] .
rph
tylko po to, żeby wszystko wyprostować: + = daje lepszą wydajność niż rozszerzanie lub dołączanie, o ile dane wejściowe są we właściwym formacie. W tym przykładzie potrzeba czasu, aby utworzyć listę [„coś”]. + = jest około 15% szybsze
Joe
@Joe Jeśli porównujesz appendz +=, to musisz uwzględnić tworzenie listy jako część pomiaru. W przeciwnym razie byłoby inne pytanie ( extendvs +=).
jamesdlin
@jamesdlin yup! Ale łatwo się pomylić, chyba że już o tym wiesz. Trochę dodatkowej precyzji nigdy nikomu nie zaszkodziło, prawda?
Joe
48

W podanym przykładzie nie ma różnicy pod względem wydajności między appenda +=. Ale jest różnica między appendi +(o którą pierwotnie zadawano pytanie).

>>> a = []
>>> id(a)
11814312
>>> a.append("hello")
>>> id(a)
11814312

>>> b = []
>>> id(b)
11828720
>>> c = b + ["hello"]
>>> id(c)
11833752
>>> b += ["hello"]
>>> id(b)
11828720

Jak widać, appendi +=ten sam wynik; dodają pozycję do listy bez tworzenia nowej listy. Użycie +dodaje dwie listy i tworzy nową listę.

DNS
źródło
Jest to różnica między append i + =.
Constantin
3
Fakt, że appenddodaje jeden wpis do listy, podczas gdy + = dodaje tyle, ile jest na drugiej liście (tj. Aliasy do extend). Ale on / ona już o tym wie, sądząc po sposobie napisania pytania. Czy jest jakaś inna różnica, której brakuje?
DNS
1
Jest różnica, ponieważ zadanie rozszerzone wprowadza ponowne wiązanie (wyjaśnienie w mojej odpowiedzi).
bobince
42
>>> a=[]
>>> a.append([1,2])
>>> a
[[1, 2]]
>>> a=[]
>>> a+=[1,2]
>>> a
[1, 2]

Zobacz, że append dodaje pojedynczy element do listy, którym może być cokolwiek. +=[]dołącza do list.

dwc
źródło
2
Głosowanie za tym, ponieważ jest to ważna różnica między nimi. Dobra robota.
31

+ = to przypisanie. Kiedy go używasz, naprawdę mówisz „some_list2 = some_list2 + [„ something ”]”. Zadania wymagają ponownego wiązania, więc:

l= []

def a1(x):
    l.append(x) # works

def a2(x):
    l= l+[x] # assign to l, makes l local
             # so attempt to read l for addition gives UnboundLocalError

def a3(x):
    l+= [x]  # fails for the same reason

Operator + = powinien również normalnie utworzyć nowy obiekt listy, tak jak zwykle robi to list + list:

>>> l1= []
>>> l2= l1

>>> l1.append('x')
>>> l1 is l2
True

>>> l1= l1+['x']
>>> l1 is l2
False

Jednak w rzeczywistości:

>>> l2= l1
>>> l1+= ['x']
>>> l1 is l2
True

Dzieje się tak, ponieważ listy Pythona implementują __iadd __ (), aby zamiast tego utworzyć + = rozszerzone zwarcie przypisania i wywołać list.extend (). (To trochę dziwna brodawka: zwykle robi to, co miałeś na myśli, ale z niejasnych powodów.)

Ogólnie rzecz biorąc, jeśli dołączasz / rozszerzasz istniejącą listę i chcesz zachować odniesienie do tej samej listy (zamiast tworzyć nową), najlepiej jest wyrazić się jednoznacznie i trzymać się metody append () / extension () metody.

bobince
źródło
21
 some_list2 += ["something"]

jest aktualne

 some_list2.extend(["something"])

dla jednej wartości nie ma różnicy. Dokumentacja stwierdza, że:

s.append(x) tak s[len(s):len(s)] = [x]
s.extend(x) samo jaks[len(s):len(s)] = x

Zatem oczywiście s.append(x)to samo cos.extend([x])

vartec
źródło
s.append przyjmuje dowolny typ i dodaje go do listy; To prawdziwy dodatek. s.extend przyjmuje iterowalne (zwykle listę) i łączy iterowalne w s, modyfikując adresy pamięci s. To nie to samo.
W4t3randWind
9

Różnica polega na tym, że concatenate spłaszczy wynikową listę, podczas gdy append zachowa poziomy nienaruszone:

Na przykład z:

myList = [ ]
listA = [1,2,3]
listB = ["a","b","c"]

Używając dołączania, otrzymujesz listę list:

>> myList.append(listA)
>> myList.append(listB)
>> myList
[[1,2,3],['a',b','c']]

Zamiast tego, używając konkatenacji, otrzymujesz płaską listę:

>> myList += listA + listB
>> myList
[1,2,3,"a","b","c"]
SRC2
źródło
5

Testy wydajności tutaj są nieprawidłowe:

  1. Nie powinieneś uruchamiać profilu tylko raz.
  2. Porównując append z + = [] ile razy powinieneś zadeklarować append jako funkcję lokalną.
  3. wyniki czasowe są różne w różnych wersjach Pythona: 64 i 32 bit

na przykład

timeit.Timer ('for i in xrange (100): app (i)', 's = []; app = s.append'). timeit ()

dobre testy można znaleźć tutaj: http://markandclick.com/1/post/2012/01/python-list-append-vs.html

Michael
źródło
nadal, testy + = na tej stronie używają += [one_var]. Jeśli pominiemy tworzenie list, + = stanie się najszybszą opcją.
Joe,
3

Oprócz aspektów opisanych w innych odpowiedziach, dołączanie i + [] zachowują się zupełnie inaczej, gdy próbujesz zbudować listę list.

>>> list1=[[1,2],[3,4]]
>>> list2=[5,6]
>>> list3=list1+list2
>>> list3
[[1, 2], [3, 4], 5, 6]
>>> list1.append(list2)
>>> list1
[[1, 2], [3, 4], [5, 6]]

lista1 + ['5', '6'] dodaje '5' i '6' do listy1 jako pojedyncze elementy. list1.append (['5', '6']) dodaje listę ['5', '6'] do list1 jako pojedynczy element.

Chris Upchurch
źródło
2

Ponowne wiązanie, o którym mowa w innych odpowiedziach, ma znaczenie w pewnych okolicznościach:

>>> a = ([],[])
>>> a[0].append(1)
>>> a
([1], [])
>>> a[1] += [1]
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Dzieje się tak, ponieważ przypisanie rozszerzone zawsze powoduje ponowne wiązanie, nawet jeśli obiekt został zmutowany w miejscu. Tak się składa, że ​​następuje ponowne wiązanie a[1] = *mutated list*, które nie działa w przypadku krotek.

Przywróć Monikę
źródło
0

weźmy najpierw przykład

list1=[1,2,3,4]
list2=list1     (that means they points to same object)

if we do 
list1=list1+[5]    it will create a new object of list
print(list1)       output [1,2,3,4,5] 
print(list2)       output [1,2,3,4]

but if we append  then 
list1.append(5)     no new object of list created
print(list1)       output [1,2,3,4,5] 
print(list2)       output [1,2,3,4,5]

extend(list) also do the same work as append it just append a list instead of a 
single variable 
Avnish kumar
źródło
0

Metoda append () dodaje pojedynczy element do istniejącej listy

some_list1 = []
some_list1.append("something")

Więc tutaj some_list1 zostanie zmodyfikowane.

Zaktualizowano:

Natomiast użycie + do łączenia elementów list (więcej niż jednego elementu) w istniejącej liście jest podobne do rozszerzenia (poprawione przez Flux ).

some_list2 = []
some_list2 += ["something"]

Więc tutaj some_list2 i ["coś"] to dwie listy, które są połączone.

Naveen Verma
źródło
1
To jest źle. +=nie zwraca nowej listy. Programowanie FAQ mówi: „... na listach, __iadd__jest równoznaczne z wywołaniem extendna listy i powrót do listy Dlatego mówimy, że na listach. +=Jest«skrótem»za list.extend”. Możesz to również zobaczyć na własne oczy w
Flux
0

„+” nie zmienia listy

.append () mutuje starej listy

Andres
źródło