Python aynı değere birden çok değişken mi atıyor? liste davranışı


132

Değişkenleri başlatmak için aşağıda gösterildiği gibi çoklu atama kullanmayı denedim, ancak davranıştan dolayı kafam karıştı, değerler listesini ayrı ayrı yeniden atamayı bekliyorum, yani b [0] ve c [0] eşittir 0.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

Sonuç: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Bu doğru mu? çoklu atama için ne kullanmalıyım? bundan farklı olan nedir?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

sonuç: ('f:', 3) ('e:', 4)


2
Eğer istiyorsunuz a, bve c,(bu durumda bir liste) aynı değere tüm noktasına veya istediğiniz yapmak a=0, b=3ve c=5. Bu durumda ister istersin a,b,c = [0,3,5]ister sadece a,b,c = 0,3,5.
chepner

Yanıtlar:


271

Python'a C / Java / vb. Bir dilden geliyorsanız. aile, abir "değişken" olarak düşünmeyi bırakmanıza ve onu bir "isim" olarak düşünmeye başlamanıza yardımcı olabilir .

a, bVec eşit değerlere sahip farklı değişkenler değildir; aynı değer için farklı isimler. Değişkenlerin türleri, kimlikleri, adresleri ve bunun gibi her türlü şeyi vardır.

İsimler bunlardan hiçbirine sahip değil. Değerler elbette var ve aynı değer için birçok isme sahip olabilirsiniz.

Notorious B.I.G.Sosisli sandviç verirseniz * Biggie Smallsve Chris Wallacesosisli sandviçiniz varsa. Öğesinin ilk öğesini a1 olarak değiştirirseniz, öğesinin ilk öğeleri bve c1 olur.

İki adın aynı nesneyi adlandırıp adlandırmadığını öğrenmek istiyorsanız, isoperatörü kullanın :

>>> a=b=c=[0,3,5]
>>> a is b
True

Sonra soruyorsun:

bundan farklı olan nedir?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Burada, adı edeğere yeniden bağlıyorsunuz 4. Bu isimleri etkilemez dvef hiçbir şekilde .

Önceki sürümde, atama edildi a[0], değil a. Yani, bakış açısından a[0], yeniden bağlanıyorsun a[0], ama bakış açısından a, onu yerinde değiştiriyorsun.

Size idbir nesnenin kimliğini temsil eden benzersiz bir sayı veren işlevi, isyardımcı olamadığınızda bile tam olarak hangi nesnenin olduğunu görmek için kullanabilirsiniz:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

a[0]4297261120'den 4297261216'ya değiştiğine dikkat edin — artık farklı bir değer için bir ad. Ve b[0]şimdi aynı yeni değer için bir isim. Çünkü ave bhala aynı nesneyi adlandırıyorlar.


Kapakların altında a[0]=1, aslında liste nesnesinde bir yöntem çağırıyor. ( Eşittir a.__setitem__(0, 1).) Yani, gerçekten hiçbir şeyi yeniden bağlamıyor. Aramak gibi my_object.set_something(1). Elbette, muhtemelen nesne bu yöntemi uygulamak için bir örnek özniteliğini yeniden bağlamaktadır, ancak önemli olan bu değildir; önemli olan hiçbir şey atamamanız, sadece nesneyi değiştirmenizdir. Ve ile aynı a[0]=1.


user570826 sordu:

Ya sahipsek a = b = c = 10

Bu tamamen aynı durum a = b = c = [1, 2, 3] : aynı değer için üç adınız var.

Ancak bu durumda, değer an'dır intve ints değişmezdir. Her iki durumda da, afarklı bir değere (örneğin a = "Now I'm a string!") yeniden bağlanabilirsiniz , ancak bu, orijinal değeri etkilemez.b ve chala için isimler olacak. Aradaki fark, bir listeyle değeri [1, 2, 3]olarak değiştirebilmenizdir [1, 2, 3, 4], örneğin a.append(4); çünkü bu aslında bve cisimleri olan değeri değiştiriyor, bşimdi b olacaktır [1, 2, 3, 4]. Değeri 10başka bir şeye değiştirmenin bir yolu yok. 10sonsuza kadar 10, tıpkı Claudia gibi, vampir sonsuza kadar 5 yaşında (en azından onun yerine Kirsten Dunst gelene kadar).


* Uyarı: Notorious BIG sosisli sandviç vermeyin. Gangsta rap zombileri asla gece yarısından sonra beslenmemelidir.


Ya biz have, a = b = c = 10;ve b'nin değerini güncellemeye çalıştığımızda, bu diğerlerini etkiler? kimliklerinin aynı olduğunu kontrol etsem de.?
AJ

@ user570826: 10değişmezdir — bu, değeri güncellemenin bir yolu olmadığı anlamına gelir, bu nedenle sorunuz bir anlam ifade etmez. bFarklı bir değere işaret edebilirsiniz , ancak bunu yapmanın üzerinde hiçbir etkisi yoktur ave chala orijinal değeri işaret etmektedir. Listelerin yarattığı fark, değiştirilebilir olmalarıdır - örneğin, appendbir listeye girebilirsiniz veya lst[0] = 3bu, o değer için tüm isimlerden görülebilecek olan değeri güncelleyecektir.
abarnert

72

Öksürük öksürük

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

10
IMHO, bu aslında OP'nin çoklu ödev için ne kullanmam gerektiği konusundaki ilk anahtar sorusunu yanıtlarken, yukarıdaki daha yüksek puanlı ve daha serebral cevap vermiyor.
Will Croxford

2
Veya a,b,c = 1,2,3parantez olmadan Python 2 veya 3'te çalışır, eğer gerçekten fazladan cm okunabilirlik istiyorsanız.
Will Croxford

14

Evet, beklenen davranış budur. a, b ve c'nin tümü aynı liste için etiket olarak ayarlanmıştır. Üç farklı liste istiyorsanız, bunları ayrı ayrı atamanız gerekir. Ya açık listeyi tekrarlayabilir ya da bir listeyi kopyalamak için çeşitli yollardan birini kullanabilirsiniz:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Python'daki atama ifadeleri nesneleri kopyalamaz - adı bir nesneye bağlarlar ve bir nesnenin ayarladığınız sayıda etiketi olabilir. İlk düzenlemenizde, a [0] 'ı değiştirerek, a, b ve c'nin hepsinin başvurduğu tek listenin bir öğesini güncelliyorsunuz. İkinci olarak, e'yi değiştirirken, e'yi farklı bir nesne için bir etiket haline getiriyorsunuz (3 yerine 4).


13

Python'da her şey bir nesnedir, ayrıca "basit" değişken türleri (int, float, vb.).

Bir değişken değeri değiştirdiğinizde, aslında işaretçisini değiştirirsiniz ve iki değişken arasında karşılaştırma yaparsanız, onların işaretçilerini karşılaştırır. . (Açık olmak gerekirse, işaretçi bir değişkenin depolandığı fiziksel bilgisayar belleğindeki adrestir).

Sonuç olarak, bir iç değişken değerini değiştirdiğinizde, bellekteki değerini değiştirirsiniz ve bu, bu adrese işaret eden tüm değişkenleri etkiler.

Örneğiniz için, yaptığınız zaman:

a = b =  5 

Bu, a ve b'nin 5 değerini içeren bellekteki aynı adresi gösterdiği anlamına gelir, ancak bunu yaptığınızda:

a = 6

Bu, b'yi etkilemez çünkü a şimdi 6'yı içeren başka bir bellek konumuna işaret ediyor ve b hala 5'i içeren bellek adresini gösteriyor.

Ama yaptığınızda:

a = b = [1,2,3]

a ve b, yine aynı konuma işaret eder, ancak fark şudur ki, liste değerlerinden birini değiştirirseniz:

a[0] = 2

Bu, a'nın işaret ettiği belleğin değerini değiştirir, ancak a yine de b ile aynı adresi gösterir ve sonuç olarak b de değişir.


6
Bu oldukça yanıltıcıdır. İşaretçiler Python seviyesinde kesinlikle görünmezler ve dört ana uygulamadan (PyPy ve Jython) en az ikisi uygulama içinde bile bunları kullanmaz.
abarnert

1
Python içsellerini okuyup keşfetmeye hoş geldiniz ve python'daki her değişkenin aslında işaretçi olduğunu keşfedeceksiniz.
Ori Seri

4
Hayır . Python'un (CPython) bir uygulamasında , her değişken bir PyObject. PyPy veya Jython gibi diğer uygulamalarda bu doğru değildir. (Aslında, bunun nasıl doğru olabileceği bile net değil , çünkü bu uygulamaların yazıldığı dillerde işaret bile yok.)
abarnert

Kavramsal anlamda "işaretçi" nin kullanılmasının uygun olduğunu düşünüyorum (belki de uygulamaların değişiklik gösterebileceğine dair bir feragatname ile), özellikle amaç davranışı iletmekse.
Levon

@abarnert İnsanlar Python dediklerinde, diğer nadiren kullanılan uygulamaları değil, CPython'u kastediyorlar. Tıpkı insanların Kleenex dedikleri gibi yüz dokusunu kastediyorlar. Bu yorumlarda anlambilim oyununu oynamak gerçekten gereksiz. Yazdıklarına gelince, tarif ettiği davranış yanlış mı?
swade

10

id(name)İki adın aynı nesneyi temsil edip etmediğini kontrol etmek için kullanabilirsiniz :

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Listeler değiştirilebilir; bu, yeni bir nesne oluşturmadan değeri yerinde değiştirebileceğiniz anlamına gelir. Ancak, değeri nasıl değiştirdiğinize bağlıdır:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Yeni liste için atarsanız ao etkilemez böylece, o zaman onun kimliği değişecek bve c'nın değerleri:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Tamsayılar değişmezdir, bu nedenle yeni bir nesne oluşturmadan değeri değiştiremezsiniz:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1

1
idmutlaka bir hafıza konumu değildir. gibi docs demek, bu "kullanım süresi boyunca bu nesne için benzersiz ve sürekli olması sağlanır kimliğini ... Bir tamsayı ...." Döndürür CPython bellek adresini kullanır id, ancak diğer Python uygulamaları olmayabilir. Örneğin PyPy bunu yapmaz. Ve "iki değişken aynı hafıza konumuna işaret ediyor" demek, onu C tarzı anlayanlar için yanıltıcıdır. "Aynı nesne için iki isim" hem daha doğru hem de daha az yanıltıcıdır.
abarnert

@abarnert açıklama için teşekkürler, cevabı güncelledim.
jurgenreza

7

ilk örneğinizde a = b = c = [1, 2, 3]gerçekten diyorsunuz:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

'A'yı 1'e,' b'yi '2'ye ve' c'yi 3'e eşit olarak ayarlamak istiyorsanız, şunu deneyin:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

Bu yardımcı olur umarım!


4

Basitçe söylemek gerekirse, ilk durumda, bir list . Hafızada listenin yalnızca bir kopyası oluşturulur ve tüm isimler bu konumu ifade eder. Bu nedenle listeyi herhangi bir isim kullanarak değiştirmek, aslında hafızadaki listeyi değiştirecektir.

İkinci durumda, bellekte aynı değerde birden çok kopya oluşturulur. Yani her kopya birbirinden bağımsızdır.


3

İhtiyacın olan şey şudur:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)

3

İhtiyacım olan şeyi yapan kod şu olabilir:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Sonuç:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.