Python menugaskan banyak variabel ke nilai yang sama? daftar perilaku

131

Saya mencoba menggunakan beberapa penugasan seperti yang ditunjukkan di bawah ini untuk menginisialisasi variabel, tapi saya bingung dengan perilaku, saya berharap untuk menetapkan kembali daftar nilai secara terpisah, maksud saya b [0] dan c [0] sama dengan 0 seperti sebelumnya.

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

Hasilnya adalah: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Apakah itu benar? apa yang harus saya gunakan untuk banyak tugas? apa yang berbeda dari ini?

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

hasil: ('f:', 3) ('e:', 4)

Marco
sumber
2
Apakah Anda ingin a, bdan c,semua titik dengan nilai yang sama (dalam hal ini daftar), atau Anda ingin a=0, b=3dan c=5. Dalam hal ini, Anda inginkan a,b,c = [0,3,5]atau adil a,b,c = 0,3,5.
chepner

Jawaban:

271

Jika Anda datang ke Python dari bahasa di C / Java / etc. keluarga, mungkin membantu Anda untuk berhenti memikirkan asebagai "variabel", dan mulai memikirkannya sebagai "nama".

a,, bdan cbukan variabel yang berbeda dengan nilai yang sama; mereka berbeda nama untuk nilai identik yang sama. Variabel memiliki tipe, identitas, alamat, dan segala macam hal seperti itu.

Nama tidak memiliki semua itu. Nilai memang, tentu saja, dan Anda dapat memiliki banyak nama untuk nilai yang sama.

Jika Anda memberikan Notorious B.I.G.hot dog, * Biggie Smallsdan Chris Wallacemiliki hot dog. Jika Anda mengubah elemen pertama amenjadi 1, elemen pertama bdan cadalah 1.

Jika Anda ingin tahu apakah dua nama memberi nama objek yang sama, gunakan isoperator:

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

Anda kemudian bertanya:

apa yang berbeda dari ini?

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

Di sini, Anda mengubah nama emenjadi nilai 4. Itu tidak memengaruhi nama ddan fdalam cara apa pun.

Di versi Anda sebelumnya, Anda ditugaskan untuk a[0], bukan ke a. Jadi, dari sudut pandang a[0], Anda memberontak a[0], tetapi dari sudut pandang a, Anda mengubahnya di tempat.

Anda dapat menggunakan idfungsi ini, yang memberi Anda beberapa nomor unik yang mewakili identitas suatu objek, untuk melihat secara tepat objek mana yang bahkan ketika istidak dapat membantu:

>>> 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

Perhatikan bahwa a[0]telah berubah dari 4297261120 menjadi 4297261216 — sekarang nama untuk nilai yang berbeda. Dan b[0]sekarang juga menjadi nama untuk nilai baru yang sama. Itu karena adan bmasih menamai objek yang sama.


Di bawah selimut, a[0]=1sebenarnya memanggil metode pada objek daftar. (Ini setara dengan a.__setitem__(0, 1).) Jadi, itu tidak benar - benar memberontak sama sekali. Ini seperti menelepon my_object.set_something(1). Tentu, kemungkinan objek tersebut memutihkan atribut instance untuk mengimplementasikan metode ini, tetapi bukan itu yang penting; Yang penting adalah Anda tidak menetapkan apa pun, Anda hanya memutasikan objek. Dan itu sama dengan a[0]=1.


pengguna570826 bertanya:

Bagaimana jika kita punya, a = b = c = 10

Situasi yang persis sama dengan a = b = c = [1, 2, 3]: Anda memiliki tiga nama dengan nilai yang sama.

Tetapi dalam kasus ini, nilainya adalah int, dan ints tidak berubah. Dalam kedua kasus tersebut, Anda dapat mengubah nilai ake nilai yang berbeda (misalnya, a = "Now I'm a string!"), tetapi tidak akan memengaruhi nilai asli, yang bdan cmasih akan menjadi nama untuk. Perbedaannya adalah bahwa dengan daftar, Anda dapat mengubah nilainya [1, 2, 3]menjadi [1, 2, 3, 4]dengan melakukan, misalnya a.append(4),; karena itu sebenarnya mengubah nilai yang bdan cmerupakan nama untuk, bsekarang akan b [1, 2, 3, 4]. Tidak ada cara untuk mengubah nilai 10menjadi hal lain. 10adalah 10 selamanya, sama seperti Claudia vampir adalah 5 selamanya (setidaknya sampai dia digantikan oleh Kirsten Dunst).


* Peringatan: Jangan berikan hot dog Notorious BIG. Gangsta rap zombie tidak boleh diberi makan setelah tengah malam.

abarnert
sumber
Bagaimana jika kita have, a = b = c = 10;dan ketika kita mencoba memperbarui nilai b, apakah itu berpengaruh pada yang lain? meskipun saya memeriksa id mereka sama.
AJ
@ user570826: 10tidak dapat diubah — itu berarti tidak ada cara untuk memperbarui nilai, jadi pertanyaan Anda tidak masuk akal. Anda dapat menunjuk bpada nilai yang berbeda, tetapi hal itu tidak berpengaruh pada adan c, yang masih menunjuk pada nilai aslinya. Perbedaan yang dibuat oleh daftar adalah bahwa mereka dapat diubah — misalnya, Anda dapat appendke daftar, atau lst[0] = 3, dan yang akan memperbarui nilai, yang akan terlihat melalui semua nama untuk nilai itu.
abarnert
72

Uhuk uhuk

>>> 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'}
>>> 
Jimmy Kane
sumber
10
IMHO, ini sebenarnya menjawab pertanyaan kunci OP pertama tentang apa yang harus saya gunakan untuk penugasan ganda, sedangkan jawaban yang lebih tinggi dan lebih banyak otak di atas tidak.
Will Croxford
2
Atau a,b,c = 1,2,3tanpa tanda kurung bekerja di Python 2 atau 3, jika Anda benar-benar menginginkan tambahan cm kesesuaian.
Will Croxford
14

Ya, itulah perilaku yang diharapkan. a, b dan c semuanya ditetapkan sebagai label untuk daftar yang sama. Jika Anda ingin tiga daftar berbeda, Anda perlu menetapkannya secara individual. Anda bisa mengulang daftar eksplisit, atau menggunakan salah satu dari banyak cara untuk menyalin daftar:

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

Pernyataan penugasan dengan Python tidak menyalin objek - mereka mengikat nama ke objek, dan objek dapat memiliki label sebanyak yang Anda tetapkan. Di edit pertama Anda, mengubah [0], Anda memperbarui satu elemen dari daftar tunggal yang dirujuk oleh a, b, dan c. Di e kedua Anda, mengubah e, ​​Anda beralih e menjadi label untuk objek yang berbeda (4 bukannya 3).

Peter DeGlopper
sumber
13

Dalam python, semuanya adalah objek, juga tipe variabel "sederhana" (int, float, dll.).

Ketika Anda mengubah nilai variabel, Anda benar-benar mengubah pointer itu , dan jika Anda membandingkan antara dua variabel itu membandingkan pointer mereka . (Agar jelas, pointer adalah alamat di memori komputer fisik tempat variabel disimpan).

Akibatnya, ketika Anda mengubah nilai variabel dalam, Anda mengubah nilainya di memori dan itu mempengaruhi semua variabel yang mengarah ke alamat ini.

Sebagai contoh Anda, ketika Anda melakukannya:

a = b =  5 

Ini berarti bahwa a dan b menunjuk ke alamat yang sama dalam memori yang berisi nilai 5, tetapi ketika Anda melakukannya:

a = 6

Itu tidak mempengaruhi b karena a sekarang menunjuk ke lokasi memori lain yang berisi 6 dan b masih menunjuk ke alamat memori yang berisi 5.

Tapi, ketika Anda melakukannya:

a = b = [1,2,3]

a dan b, sekali lagi, menunjuk ke lokasi yang sama tetapi perbedaannya adalah jika Anda mengubah salah satu dari nilai daftar:

a[0] = 2

Ini mengubah nilai dari memori yang menunjuk pada a, tetapi a masih menunjuk ke alamat yang sama dengan b, dan sebagai hasilnya, b berubah juga.

Ori Seri
sumber
6
Ini sangat menyesatkan. Pointer tentu tidak terlihat di tingkat Python, dan setidaknya dua dari empat implementasi utama (PyPy dan Jython) tidak menggunakannya bahkan di dalam implementasi.
abarnert
1
Anda dipersilakan untuk membaca dan menjelajahi internal python dan Anda akan menemukan bahwa setiap variabel dalam python sebenarnya adalah pointer.
Ori Seri
4
Tidak. Dalam satu implementasi Python (CPython), setiap variabel adalah pointer ke a PyObject. Itu tidak benar dalam implementasi lain seperti PyPy atau Jython. (Faktanya, bahkan tidak jelas bagaimana itu bisa benar, karena bahasa-bahasa implementasi tersebut ditulis bahkan tidak memiliki petunjuk.)
abarnert
Saya pikir penggunaan "pointer" dalam pengertian konseptual adalah ok (mungkin dengan penafian bahwa implementasi dapat bervariasi), terutama jika tujuannya adalah untuk menyampaikan perilaku.
Levon
@abarnert Ketika orang mengatakan Python yang mereka maksud adalah CPython, bukan implementasi lain yang jarang digunakan. Sama seperti ketika orang mengatakan tisu artinya jaringan wajah. Memainkan permainan semantik dalam komentar ini benar-benar tidak perlu. Adapun apa yang dia tulis, apakah perilaku yang dia gambarkan salah?
bersumpah
10

Anda dapat menggunakan id(name)untuk memeriksa apakah dua nama mewakili objek yang sama:

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

Daftar bisa berubah; itu berarti Anda dapat mengubah nilai di tempatnya tanpa membuat objek baru. Namun, itu tergantung pada bagaimana Anda mengubah nilainya:

>>> 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]

Jika Anda menetapkan daftar baru a, maka id akan berubah, sehingga tidak akan memengaruhi bdan cnilainya:

>>> 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]

Integer tidak dapat diubah, sehingga Anda tidak dapat mengubah nilai tanpa membuat objek baru:

>>> 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
jurgenreza
sumber
1
idbelum tentu lokasi memori. Seperti yang dikatakan oleh dokumen , ini mengembalikan "identitas ... bilangan bulat ... yang dijamin unik dan konstan untuk objek ini selama masa pakainya." CPython kebetulan menggunakan alamat memori sebagai id, tetapi implementasi Python lainnya mungkin tidak. PyPy, misalnya, tidak. Dan mengatakan "dua vars menunjuk ke lokasi memori yang sama" menyesatkan siapa pun yang memahaminya C-style. "Dua nama untuk objek yang sama" keduanya lebih akurat dan tidak terlalu menyesatkan.
abarnert
@abarnert terima kasih atas klarifikasi, saya memperbarui jawabannya.
jurgenreza
7

dalam contoh pertama a = b = c = [1, 2, 3]Anda, Anda benar-benar mengatakan:

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

Jika Anda ingin menetapkan 'a' sama dengan 1, 'b' sama dengan '2' dan 'c' sama dengan 3, coba ini:

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

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

Semoga ini membantu!

Nick Burns
sumber
4

Sederhananya, dalam kasus pertama, Anda menetapkan beberapa nama ke a list. Hanya satu salinan daftar yang dibuat dalam memori dan semua nama merujuk ke lokasi itu. Jadi mengubah daftar menggunakan salah satu nama sebenarnya akan mengubah daftar dalam memori.

Dalam kasus kedua, banyak salinan dengan nilai yang sama dibuat dalam memori. Jadi setiap salinan tidak tergantung satu sama lain.

Vara
sumber
3

Yang Anda butuhkan adalah ini:

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)
pydsigner
sumber
3

Kode yang melakukan apa yang saya butuhkan adalah:

# 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)

Hasil:

('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])
Marco
sumber