Saya mencoba memahami pendekatan Python untuk lingkup variabel. Dalam contoh ini, mengapa f()
bisa mengubah nilai x
, seperti yang dirasakan di dalam main()
, tetapi bukan nilai n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Keluaran:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Jawaban:
Beberapa jawaban berisi kata "salin" dalam konteks panggilan fungsi. Saya merasa ini membingungkan.
Python tidak menyalin objek Anda lulus selama panggilan fungsi yang pernah .
Parameter fungsi adalah nama . Saat Anda memanggil fungsi, Python mengikat parameter-parameter ini ke objek apa pun yang Anda lewati (melalui nama dalam lingkup pemanggil).
Objek bisa berubah-ubah (seperti daftar) atau tidak berubah (seperti bilangan bulat, string dengan Python). Objek yang bisa berubah yang dapat Anda ubah. Anda tidak dapat mengubah nama, Anda hanya dapat mengikatnya ke objek lain.
Contoh Anda bukan tentang cakupan atau ruang nama , ini tentang penamaan dan pengikatan serta mutabilitas suatu objek dengan Python.
Berikut adalah gambar-gambar bagus tentang perbedaan antara variabel dalam bahasa lain dan nama-nama dengan Python .
sumber
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
dalamf()
tidak berpengaruh pada daftarx
di fungsi utama. Saya sudah memperbarui komentar, untuk membuatnya lebih spesifik.Anda sudah mendapatkan sejumlah jawaban, dan saya secara luas setuju dengan JF Sebastian, tetapi Anda mungkin menemukan ini berguna sebagai jalan pintas:
Setiap kali Anda melihat
varname =
, Anda membuat nama baru yang mengikat dalam ruang lingkup fungsi. Nilai apa punvarname
yang terikat sebelumnya hilang dalam lingkup ini .Setiap kali Anda melihat
varname.foo()
Anda memanggil metodevarname
. Metode ini dapat mengubah varname (misalnyalist.append
).varname
(atau, lebih tepatnya, objek yangvarname
namanya) mungkin ada di lebih dari satu cakupan, dan karena itu adalah objek yang sama, setiap perubahan akan terlihat di semua lingkup.[perhatikan bahwa
global
kata kunci membuat pengecualian pada kasus pertama]sumber
f
sebenarnya tidak mengubah nilaix
(yang selalu merupakan referensi yang sama dengan turunan daftar). Sebaliknya, itu mengubah isi daftar ini.Dalam kedua kasus, salinan referensi diteruskan ke fungsi. Di dalam fungsi,
n
mendapat nilai baru. Hanya referensi di dalam fungsi yang diubah, bukan yang di luarnya.x
tidak mendapatkan nilai baru: referensi di dalam maupun di luar fungsi tidak dimodifikasi. Sebaliknya,x
's nilai dimodifikasi.Karena keduanya di
x
dalam fungsi dan di luarnya merujuk pada nilai yang sama, keduanya melihat modifikasi. Sebaliknya, din
dalam fungsi dan di luarnya merujuk ke nilai yang berbeda setelahn
dipindahkan di dalam fungsi.sumber
Saya akan mengganti nama variabel untuk mengurangi kebingungan. n -> nf atau nmain . x -> xf atau xmain :
Ketika Anda memanggil fungsi f , runtime Python membuat salinan xmain dan menetapkannya ke xf , dan juga menetapkan salinan nmain ke nf .
Dalam hal n , nilai yang disalin adalah 1.
Dalam kasus x nilai yang disalin bukanlah daftar literal [0, 1, 2, 3] . Ini adalah referensi ke daftar itu. xf dan xmain menunjuk pada daftar yang sama, jadi ketika Anda memodifikasi xf, Anda juga memodifikasi xmain .
Namun, jika Anda menulis sesuatu seperti:
Anda akan menemukan bahwa xmain belum berubah. Ini karena, pada baris xf = ["foo", "bar"] Anda harus mengubah xf untuk menunjuk ke daftar baru . Setiap perubahan yang Anda lakukan pada daftar baru ini tidak akan berpengaruh pada daftar yang tetap ada masih poin ke.
Semoga itu bisa membantu. :-)
sumber
nf = 2
, di mana namanyanf
diubah menjadi point to2
. Angka tidak berubah, daftar bisa berubah.Itu karena daftar adalah objek yang bisa berubah. Anda tidak menyetel x ke nilai [0,1,2,3], Anda mendefinisikan label pada objek [0,1,2,3].
Anda harus mendeklarasikan fungsi Anda f () seperti ini:
sumber
x = x + [4]
bukanx.append(4)
, Anda akan melihat ada perubahan dalam penelepon juga meskipun daftar bisa berubah. Ini ada hubungannya dengan jika memang bermutasi.x += [4]
makax
dimutasi, seperti apa yang terjadix.append(4)
, sehingga penelepon akan melihat perubahan.n adalah int (tidak dapat diubah), dan salinannya diteruskan ke fungsi, jadi pada fungsi tersebut Anda mengubah salinannya.
X adalah daftar (dapat diubah), dan salinan dari pointer dilewatkan o fungsi sehingga x.append (4) mengubah isi daftar. Namun, Anda mengatakan x = [0,1,2,3,4] dalam fungsi Anda, Anda tidak akan mengubah konten x pada main ().
sumber
Jika fungsi yang ditulis ulang dengan variabel yang sama sekali berbeda dan kita sebut id pada mereka, kemudian menggambarkan titik dengan baik. Saya tidak mendapatkan ini pada awalnya dan membaca posting jfs dengan penjelasan yang bagus , jadi saya mencoba untuk memahami / meyakinkan diri saya sendiri:
z dan x memiliki id yang sama. Hanya beda tag untuk struktur dasar yang sama dengan kata artikel.
sumber
Python adalah bahasa murni nilai demi nilai jika Anda memikirkannya dengan benar. Variabel python menyimpan lokasi objek dalam memori. Variabel Python tidak menyimpan objek itu sendiri. Saat Anda meneruskan variabel ke suatu fungsi, Anda meneruskan salinan alamat objek yang ditunjuk oleh variabel.
Kontras dua fungsi ini
Sekarang, saat Anda mengetik ke dalam shell
Bandingkan ini dengan goo.
Dalam kasus pertama, kami mengirimkan salinan alamat sapi ke foo dan foo memodifikasi keadaan objek yang berada di sana. Objek akan dimodifikasi.
Dalam kasus kedua, Anda menyerahkan salinan alamat sapi ke goo. Kemudian goo mulai mengubah salinan itu. Efek: tidak ada.
Saya menyebutnya ini prinsip rumah merah muda . Jika Anda membuat salinan alamat Anda dan memberi tahu seorang pelukis untuk mengecat rumah itu dengan alamat itu, Anda akan berakhir dengan sebuah rumah merah muda. Jika Anda memberi pelukis salinan alamat Anda dan menyuruhnya untuk mengubahnya ke alamat baru, alamat rumah Anda tidak akan berubah.
Penjelasannya menghilangkan banyak kebingungan. Python melewati variabel alamat yang disimpan oleh nilai.
sumber
Python disalin oleh nilai referensi. Objek menempati bidang dalam memori, dan referensi dikaitkan dengan objek itu, tetapi itu sendiri menempati bidang dalam memori. Dan nama / nilai dikaitkan dengan referensi. Dalam fungsi python, selalu menyalin nilai referensi, jadi dalam kode Anda, n disalin menjadi nama baru, ketika Anda menetapkan itu, ia memiliki ruang baru di tumpukan pemanggil. Tetapi untuk daftar, namanya juga disalin, tetapi merujuk pada memori yang sama (karena Anda tidak pernah menetapkan nilai baru pada daftar). Itu adalah keajaiban dalam python!
sumber
Pemahaman umum saya adalah bahwa variabel objek apa saja (seperti daftar atau dict, antara lain) dapat dimodifikasi melalui fungsinya. Apa yang saya yakin tidak bisa Anda lakukan adalah menetapkan kembali parameter - yaitu, tetapkan dengan referensi dalam fungsi yang bisa dipanggil.
Itu konsisten dengan banyak bahasa lain.
Jalankan skrip pendek berikut untuk melihat cara kerjanya:
sumber
Saya telah memodifikasi jawaban saya beberapa kali dan menyadari bahwa saya tidak perlu mengatakan apa-apa, python sudah menjelaskan sendiri.
Setan ini bukan referensi / nilai / bisa berubah atau tidak / contoh, ruang nama atau variabel / daftar atau str, IT IS THE SYNTAX, EQUAL SIGN.
sumber