2011-07-22 11 views
17

Gdy uruchomiony skrypt (Python V2.6):wybór zmiennej i modyfikacji (na pytona)

a = [1,2] 
b = a 
a.append(3) 
print a 
>>>> [1,2,3] 
print b 
>>>> [1,2,3] 

że oczekiwany print b wyjściowe [1,2]. Dlaczego b się zmieniło, gdy zmieniłem tylko? Czy b jest na stałe związany z? Jeśli tak, czy mogę je uniezależnić? W jaki sposób?

+1

możliwe duplikat [Jak sklonować listę w Pythonie?] (Http://stackoverflow.com/questions/2612802/how-to-clone-a-list-in-python) –

+3

@Felix Powiedziałbym, że nie do końca dup - nie pyta tylko, jak klonować, ale raczej dlaczego Python zachowuje się w określony sposób. – thegrinner

+3

"[Python ma nazwy] (http://python.net/~goodger/projects/pycon/2007/idiomatic/presentation.html#python-has-names)" jest bardzo dobrą częścią jeszcze lepszego samouczka/wprowadzenia w związku z tym zachowanie. – nagisa

Odpowiedz

36

zarządzanie pamięcią w Pythonie polega prywatną lokalizację pamięci sterty zawierający wszystkie obiekty Python i struktur danych.

Środowisko wykonawcze języka Python zajmuje się tylko odniesieniami do obiektów (które wszystkie żyją w stercie): to, co dzieje się na stosie Pythona, zawsze odnosi się do wartości, które znajdują się gdzie indziej.

>>> a = [1, 2] 

python variables

>>> b = a 

python variables

>>> a.append(3) 

python variables

Tutaj widzimy wyraźnie, że zmienna b jest zobowiązany do tego samego obiektu jako a.

Możesz użyć operatora is do sprawdzenia, czy dwa obiekty są fizycznie takie same, co oznacza, że ​​mają ten sam adres w pamięci. Można to przetestować również przy użyciu funkcji id().

>>> a is b 
>>> True 
>>> id(a) == id(b) 
>>> True 

Tak więc, w tym przypadku, trzeba wyraźnie poprosić o kopię. Gdy to zrobisz, nie będzie już żadnego połączenia między dwoma różnymi obiektami listy.

>>> b = list(a) 
>>> a is b 
>>> False 

python variables

4

Krótka odpowiedź - wskaźniki.

Po wpisaniu b = a ustawia się b, aby zobaczyć tę samą tablicę, na którą patrzy a. Musisz utworzyć nową tablicę z kopiami elementów, aby je rozdzielić. W takim przypadku coś w rodzaju b = [n for n in a] będzie działało dobrze. Aby uzyskać bardziej złożone operacje, możesz sprawdzić numer http://docs.python.org/library/copy.html.

+2

Również 'b = a [:]' działa, i jest bardziej "pythonic" (tak przynajmniej mówią) –

12

Obiekty w Pythonie są przechowywane przez odniesienie - nie przypisujesz wartości a do b, ale wskaźnik do obiektu, na który wskazuje a.

naśladować przyznawanie przez wartość, można zrobić kopię tak:

import copy 

b = copy.copy(a) 

# now the code works as "expected" 

Bądź świadomy tego ma wady wykonania.

W przypadku tablicy, istnieje specjalna metoda, która opiera się na plastry:

b = a[:] 

# code also works as expected here 

Aktualizacja - Poza tym, z niektórych obiektów można użyć konstruktora-ta obejmuje listy:

b = list(a) 
+2

DiggyF wskazał to w komentarzu poniżej - będziesz chciał zastąpić 'b = a [:]' z ' b = list (a) 'ze względu na czytelność i wszystko, co działa z numpy. – thegrinner

3

a jest wskaźnikiem do listy [1,2].

Po wykonaniu przypisania b = a wartość b jest adresem listy [1,2].

Tak więc, gdy robisz a.append(3), tak naprawdę nie zmieniasz a, zmieniasz listę, na którą wskazuje a. Ponieważ a i b wskazują na tę samą listę, oba wydają się zmieniać po zmodyfikowaniu drugiego.

4

Możesz chcieć spojrzeć na link this. Problem, który tutaj masz, to: a i b oba wskazują na to samo miejsce w pamięci, więc zmiana jednego z nich powoduje zmianę drugiego.Zamiast tego, chcesz zrobić coś takiego:

a = [1,2] 
b = list(a) 
+3

Z artykułu w odnośniku: "Następnym razem, gdy zobaczysz próbę [:] zastąpienia go listą, Twój kod powinien być bardziej czytelny." Diabeł tkwi w szczegółach. ". Dlatego wskazane jest użycie 'b = list (a)'. Jest to również dobry pomysł, ponieważ kiedy ludzie przełączają się z listy na tablicę numpy, wówczas [:] będzie odwoływać się do tych samych danych. – SiggyF

+0

@DiggyF Dobry punkt. Naprawiony. – thegrinner

2

Jeśli chcesz po prostu skopiować zawartość listy A do B, zamiast dokonywania wskaźnik BA a:

b = a[:] 

pomocą operatora plasterek skopiuje zawartość listy do B takie, które staną się przykładem:

a = [1,2] 
b = a[:] 
a.append(3) 
print a 
>>>> [1,2,3] 
print b 
>>>> [1,2]