Bagaimana cara saya melewati variabel dengan referensi?

2628

Dokumentasi Python tampaknya tidak jelas tentang apakah parameter dilewatkan oleh referensi atau nilai, dan kode berikut menghasilkan nilai yang tidak berubah 'Asli'

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

Apakah ada sesuatu yang bisa saya lakukan untuk melewatkan variabel dengan referensi yang sebenarnya?

David Sykes
sumber
23
Untuk penjelasan / klarifikasi singkat, lihat jawaban pertama untuk pertanyaan stackoverflow ini . Karena string tidak dapat diubah, mereka tidak akan diubah dan variabel baru akan dibuat, sehingga variabel "luar" masih memiliki nilai yang sama.
PhilS
10
Kode dalam jawaban BlairConrad baik, tetapi penjelasan yang diberikan oleh DavidCournapeau dan DarenThomas benar.
Ethan Furman
70
Sebelum membaca jawaban yang dipilih, silakan mempertimbangkan membaca teks pendek ini . Bahasa lain memiliki "variabel", Python memiliki "nama" . Pikirkan tentang "nama" dan "objek" daripada "variabel" dan "referensi" dan Anda harus menghindari banyak masalah serupa.
lqc
24
Mengapa ini tidak perlu menggunakan kelas? Ini bukan Jawa: P
Peter R
2
solusi lain adalah dengan membuat 'referensi' pembungkus seperti ini: ref = type ('', (), {'n': 1}) stackoverflow.com/a/1123054/409638
robert

Jawaban:

2831

Argumen disahkan oleh penugasan . Alasan di balik ini ada dua:

  1. parameter yang diteruskan sebenarnya adalah referensi ke objek (tetapi referensi dilewatkan oleh nilai)
  2. beberapa tipe data bisa berubah, tetapi yang lain tidak

Begitu:

  • Jika Anda melewatkan objek yang dapat diubah ke dalam metode, metode tersebut mendapatkan referensi ke objek yang sama dan Anda dapat mengubahnya untuk kesenangan hati Anda, tetapi jika Anda mengubah referensi dalam metode tersebut, ruang lingkup luar tidak akan tahu apa-apa tentang hal itu, dan setelah itu Anda sudah selesai, referensi luar masih akan menunjuk ke objek asli.

  • Jika Anda meneruskan objek yang tidak dapat diubah ke suatu metode, Anda masih tidak dapat memutar ulang referensi luar, dan Anda bahkan tidak bisa bermutasi objek.

Untuk membuatnya lebih jelas, mari kita memiliki beberapa contoh.

Daftar - tipe yang bisa berubah

Mari kita coba memodifikasi daftar yang diteruskan ke metode:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Keluaran:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Karena parameter yang diteruskan adalah referensi outer_list, bukan salinannya, kita dapat menggunakan metode daftar mutasi untuk mengubahnya dan memiliki perubahan yang tercermin dalam lingkup luar.

Sekarang mari kita lihat apa yang terjadi ketika kita mencoba mengubah referensi yang dilewatkan sebagai parameter:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Keluaran:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Karena the_listparameter dilewatkan oleh nilai, menetapkan daftar baru untuk itu tidak berpengaruh bahwa kode di luar metode dapat melihat. Itu the_listadalah salinan outer_listreferensi, dan kami memiliki the_listmenunjuk ke daftar baru, tetapi tidak ada cara untuk mengubah di mana outer_listmenunjuk.

String - tipe yang tidak dapat diubah

Itu tidak berubah, jadi tidak ada yang bisa kita lakukan untuk mengubah isi string

Sekarang, mari kita coba untuk mengubah referensi

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Keluaran:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Sekali lagi, karena the_stringparameter dilewatkan oleh nilai, menetapkan string baru untuk itu tidak berpengaruh bahwa kode di luar metode bisa dilihat. Itu the_stringadalah salinan dari outer_stringrujukan, dan kami telah the_stringmenunjuk ke string baru, tetapi tidak ada cara untuk mengubah ke mana rujukan outer_string.

Saya harap ini membersihkan sedikit.

EDIT: Telah dicatat bahwa ini tidak menjawab pertanyaan yang awalnya diajukan oleh David, "Apakah ada yang bisa saya lakukan untuk meneruskan variabel dengan referensi yang sebenarnya?". Mari kita kerjakan itu.

Bagaimana kita mengatasi ini?

Seperti yang ditunjukkan oleh jawaban Andrea, Anda dapat mengembalikan nilai baru. Ini tidak mengubah cara segala sesuatu dilewatkan, tetapi membiarkan Anda mendapatkan informasi yang Anda inginkan:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

Jika Anda benar-benar ingin menghindari menggunakan nilai balik, Anda bisa membuat kelas untuk menyimpan nilai Anda dan meneruskannya ke fungsi atau menggunakan kelas yang ada, seperti daftar:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Meskipun ini agak rumit.

Blair Conrad
sumber
165
Maka hal yang sama ada di C, ketika Anda melewati "dengan referensi" Anda benar-benar melewati dengan nilai referensi ... Tentukan "dengan referensi": P
Andrea Ambu
104
Saya tidak yakin saya mengerti persyaratan Anda. Saya sudah keluar dari permainan C untuk sementara waktu, tetapi ketika saya berada di dalamnya, tidak ada "referensi referensi" - Anda bisa melewati berbagai hal, dan selalu melewati nilai, jadi apa pun yang ada di daftar parameter disalin. Tapi kadang-kadang masalahnya adalah pointer, yang bisa diikuti oleh sepotong memori (primitif, array, struct, apa pun), tetapi Anda tidak bisa mengubah pointer yang disalin dari lingkup luar - ketika Anda selesai dengan fungsi , pointer asli masih menunjuk ke alamat yang sama. C ++ memperkenalkan referensi, yang berperilaku berbeda.
Blair Conrad
34
@Zac Bowling Saya tidak terlalu mengerti apa yang Anda katakan relevan, dalam arti praktis, untuk jawaban ini. Jika pendatang baru Python ingin tahu tentang lewat ref / val, maka takeaway dari jawaban ini adalah: 1- Anda dapat menggunakan referensi yang diterima fungsi sebagai argumennya, untuk memodifikasi nilai 'luar' dari suatu variabel, selama karena Anda tidak menetapkan ulang parameter untuk merujuk ke objek baru. 2- Menetapkan ke tipe yang tidak dapat diubah akan selalu membuat objek baru, yang memecah referensi yang Anda miliki ke variabel luar.
Cam Jackson
11
@CamJackson, Anda perlu contoh yang lebih baik - angka juga objek tidak berubah dalam Python Selain itu, bukankah benar untuk mengatakan bahwa tugas apa pun tanpa berlangganan di sebelah kiri dari yang sederajat akan menugaskan kembali nama ke objek baru apakah itu abadi atau tidak? tidakdef Foo(alist): alist = [1,2,3] akan mengubah isi daftar dari perspektif penelepon.
Mark Ransom
59
-1. Kode yang ditampilkan baik, penjelasan tentang bagaimana sepenuhnya salah. Lihat jawaban oleh DavidCournapeau atau DarenThomas untuk penjelasan yang tepat mengapa.
Ethan Furman
685

Masalahnya berasal dari kesalahpahaman variabel apa yang ada di Python. Jika Anda terbiasa dengan sebagian besar bahasa tradisional, Anda memiliki model mental tentang apa yang terjadi dalam urutan berikut:

a = 1
a = 2

Anda yakin itu aadalah lokasi memori yang menyimpan nilai 1, kemudian diperbarui untuk menyimpan nilai 2. Bukan itu cara kerjanya dengan Python. Sebaliknya, adimulai sebagai referensi ke objek dengan nilai 1, kemudian akan dipindahkan sebagai referensi ke objek dengan nilai 2. Kedua objek tersebut dapat terus hidup berdampingan meskipun atidak mengacu pada yang pertama lagi; bahkan mereka dapat dibagikan dengan sejumlah referensi lain dalam program.

Saat Anda memanggil fungsi dengan parameter, referensi baru dibuat yang merujuk ke objek yang diteruskan. Ini terpisah dari referensi yang digunakan dalam panggilan fungsi, jadi tidak ada cara untuk memperbarui referensi itu dan membuatnya merujuk ke objek baru. Dalam contoh Anda:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variableadalah referensi ke objek string 'Original'. Ketika Anda menelepon, ChangeAnda membuat referensi kedua varke objek. Di dalam fungsi Anda menetapkan kembali referensi varke objek string yang berbeda 'Changed', tetapi referensi self.variabletersebut terpisah dan tidak berubah.

Satu-satunya cara untuk mengatasi ini adalah dengan melewatkan objek yang bisa berubah. Karena kedua referensi merujuk ke objek yang sama, setiap perubahan pada objek tercermin di kedua tempat.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'
Mark tebusan
sumber
106
Penjelasan singkat dan bagus. Paragraf Anda "Ketika Anda memanggil suatu fungsi ..." adalah salah satu penjelasan terbaik yang pernah saya dengar dari frasa yang agak samar bahwa 'parameter fungsi Python adalah referensi, dilewati oleh nilai.' Saya pikir jika Anda memahami paragraf itu saja, segala sesuatu yang lain hanya masuk akal dan mengalir sebagai kesimpulan logis dari sana. Maka Anda hanya perlu mengetahui kapan Anda membuat objek baru dan ketika Anda memodifikasi yang sudah ada.
Cam Jackson
3
Tetapi bagaimana Anda bisa menetapkan kembali referensi? Saya pikir Anda tidak dapat mengubah alamat 'var' tetapi string Anda "Berubah" sekarang akan disimpan dalam alamat memori 'var'. Deskripsi Anda membuatnya tampak seperti "Berubah" dan "Asli" menjadi bagian dari tempat yang berbeda dalam memori dan Anda hanya mengganti 'var' ke alamat yang berbeda. Apakah itu benar?
Glassjawed
9
@Glassjawed, saya pikir Anda mengerti. "Changed" dan "Original" adalah dua objek string yang berbeda pada alamat memori yang berbeda dan 'var' berubah dari menunjuk ke satu ke menunjuk ke yang lain.
Mark Ransom
menggunakan fungsi id () membantu memperjelas masalah, karena membuatnya jelas ketika Python membuat objek baru (jadi saya pikir, toh).
Tim Richardson
1
@MinhTran dalam istilah yang paling sederhana, referensi adalah sesuatu yang "merujuk" ke suatu objek. Representasi fisik dari itu kemungkinan besar adalah pointer, tetapi itu hanyalah detail implementasi. Ini benar-benar gagasan abstrak di hati.
Mark Ransom
329

Saya menemukan jawaban lain yang agak panjang dan rumit, jadi saya membuat diagram sederhana ini untuk menjelaskan cara Python memperlakukan variabel dan parameter. masukkan deskripsi gambar di sini

Zenadix
sumber
2
indah, membuatnya mudah untuk menemukan perbedaan halus yang ada tugas menengah, tidak jelas bagi penonton biasa. +1
user22866
9
Tidak masalah apakah A bisa berubah atau tidak. Jika Anda menetapkan sesuatu yang berbeda untuk B, A tidak berubah . Jika suatu objek dapat diubah, Anda dapat mengubahnya, tentu saja. Tapi itu tidak ada hubungannya dengan penugasan langsung ke sebuah nama ..
Martijn Pieters
1
@ Martijn Anda benar. Saya menghapus bagian dari jawaban yang menyebutkan kemampuan berubah-ubah. Saya tidak berpikir itu bisa menjadi lebih sederhana sekarang.
Zenadix
4
Terima kasih atas pembaruannya, jauh lebih baik! Yang membingungkan kebanyakan orang adalah penugasan untuk berlangganan; misalnya B[0] = 2, vs. penugasan langsung B = 2,.
Martijn Pieters
11
"A ditugaskan ke B." Apakah itu tidak ambigu? Saya pikir dalam bahasa Inggris biasa itu bisa berarti A=Batau B=A.
Hatshepsut
240

Ini bukan pass-by-value atau pass-by-reference - ini adalah call-by-object. Lihat ini, oleh Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

Berikut ini kutipan penting:

"... variabel [nama] bukan objek; mereka tidak dapat dilambangkan dengan variabel lain atau disebut oleh objek."

Dalam contoh Anda, ketika Changemetode dipanggil - namespace dibuat untuk itu; dan varmenjadi nama, di dalam namespace itu, untuk objek string 'Original'. Objek itu kemudian memiliki nama dalam dua ruang nama. Selanjutnya, var = 'Changed'mengikat varke objek string baru, dan dengan demikian namespace metode lupa 'Original'. Akhirnya, namespace itu dilupakan, dan string 'Changed'bersamanya.

David Cournapeau
sumber
21
Saya merasa kesulitan untuk membeli. Bagi saya sama seperti Java, parameternya adalah pointer ke objek di memori, dan pointer itu dilewatkan melalui stack, atau register.
Luciano
10
Ini tidak seperti java. Salah satu kasus di mana itu tidak sama adalah benda yang tidak berubah. Pikirkan tentang fungsi sepele lambda x: x. Terapkan ini untuk x = [1, 2, 3] dan x = (1, 2, 3). Dalam kasus pertama, nilai yang dikembalikan akan menjadi salinan input, dan identik dalam kasus kedua.
David Cournapeau
26
Tidak, ini persis seperti semantik Java untuk objek. Saya tidak yakin apa yang Anda maksud dengan "Dalam kasus pertama, nilai yang dikembalikan akan menjadi salinan input, dan identik dalam kasus kedua." tetapi pernyataan itu tampaknya tidak benar.
Mike Graham
23
Persis sama dengan di Jawa. Referensi objek dilewatkan oleh nilai. Siapa pun yang berpikir berbeda harus melampirkan kode Python untuk swapfungsi yang dapat bertukar dua referensi, seperti ini: a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]
cayhorstmann
24
Persis sama dengan Java ketika Anda melewatkan objek di Jawa. Namun, Java juga memiliki primitif, yang dilewatkan dengan menyalin nilai primitif. Jadi mereka berbeda dalam hal itu.
Claudiu
174

Pikirkan hal-hal yang diteruskan oleh penugasan alih-alih dengan referensi / berdasarkan nilai. Dengan begitu, selalu jelas, apa yang terjadi selama Anda memahami apa yang terjadi selama penugasan biasa.

Jadi, ketika meneruskan daftar ke fungsi / metode, daftar tersebut ditugaskan ke nama parameter. Menambah daftar akan menghasilkan daftar yang dimodifikasi. Penugasan kembali daftar di dalam fungsi tidak akan mengubah daftar asli, karena:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Karena jenis abadi tidak dapat dimodifikasi, mereka tampak seperti sedang melewati nilai - melewati sebuah int ke fungsi sarana menempatkan int untuk parameter fungsi ini. Anda hanya bisa menetapkan ulang itu, tetapi itu tidak akan mengubah nilai variabel asli.

Daren Thomas
sumber
3
Sekilas, jawaban ini tampaknya mengesampingkan pertanyaan awal. Setelah membaca kedua saya menyadari bahwa ini membuat masalah ini cukup jelas. Sebuah tindak lanjut yang baik untuk konsep "penugasan nama" ini dapat ditemukan di sini: Kode Seperti Pythonista: Idiomatik Python
Christian Groleau
65

Effbot (alias Fredrik Lundh) telah menggambarkan gaya passing variabel Python sebagai call-by-object: http://effbot.org/zone/call-by-object.htm

Objek dialokasikan pada heap dan pointer ke mereka dapat diedarkan di mana saja.

  • Saat Anda membuat tugas seperti x = 1000, entri kamus dibuat yang memetakan string "x" di namespace saat ini ke pointer ke objek integer yang berisi seribu.

  • Saat Anda memperbarui "x" dengan x = 2000, objek integer baru dibuat dan kamus diperbarui untuk menunjuk pada objek baru. Seribu objek lama tidak berubah (dan mungkin hidup atau tidak tergantung pada apakah benda lain merujuk pada objek tersebut).

  • Ketika Anda melakukan tugas baru seperti y = x, entri kamus baru "y" dibuat yang menunjuk ke objek yang sama dengan entri untuk "x".

  • Objek seperti string dan integer tidak dapat diubah . Ini berarti bahwa tidak ada metode yang dapat mengubah objek setelah dibuat. Misalnya, setelah objek integer seribu dibuat, itu tidak akan pernah berubah. Matematika dilakukan dengan membuat objek integer baru.

  • Objek seperti daftar bisa berubah . Ini berarti bahwa isi objek dapat diubah dengan apa pun yang menunjuk ke objek. Misalnya x = []; y = x; x.append(10); print yakan dicetak [10]. Daftar kosong telah dibuat. Baik "x" dan "y" menunjuk ke daftar yang sama. The append metode bermutasi (update) obyek daftar (seperti menambahkan catatan ke database) dan hasilnya adalah terlihat baik "x" dan "y" (seperti update database akan terlihat untuk setiap koneksi ke database itu).

Harapan yang menjelaskan masalah ini untuk Anda.

Raymond Hettinger
sumber
2
Saya sangat menghargai belajar tentang ini dari pengembang. Apakah benar bahwa id()fungsi mengembalikan nilai (referensi objek) pointer, seperti jawaban pepr menyarankan?
Jujur Abe
4
@HonestAbe Ya, di CPython id () mengembalikan alamat. Tetapi dalam ular lain seperti PyPy dan Jython, id () hanyalah pengidentifikasi objek yang unik.
Raymond Hettinger
59

Secara teknis, Python selalu menggunakan nilai referensi by pass . Saya akan mengulangi jawaban saya yang lain untuk mendukung pernyataan saya.

Python selalu menggunakan nilai pass-by-reference. Tidak ada pengecualian. Setiap penugasan variabel berarti menyalin nilai referensi. Tanpa pengecualian. Variabel apa pun adalah nama yang terikat pada nilai referensi. Selalu.

Anda dapat memikirkan nilai referensi sebagai alamat objek target. Alamat tersebut secara otomatis direferensikan saat digunakan. Dengan cara ini, bekerja dengan nilai referensi, sepertinya Anda bekerja secara langsung dengan objek target. Tapi selalu ada referensi di antaranya, satu langkah lagi untuk melompat ke sasaran.

Berikut adalah contoh yang membuktikan bahwa Python menggunakan passing dengan referensi:

Contoh ilustrasinya lewat argumen

Jika argumen diteruskan oleh nilai, bagian luar lsttidak dapat dimodifikasi. Hijau adalah objek target (hitam adalah nilai yang disimpan di dalam, merah adalah jenis objek), kuning adalah memori dengan nilai referensi di dalam - digambar sebagai panah. Panah solid biru adalah nilai referensi yang diteruskan ke fungsi (melalui jalur panah biru putus-putus). Kuning gelap jelek adalah kamus internal. (Ini sebenarnya bisa digambarkan juga sebagai elips hijau. Warna dan bentuknya hanya mengatakan itu internal.)

Anda dapat menggunakan id()fungsi bawaan untuk mempelajari apa nilai referensi itu (yaitu, alamat objek target).

Dalam bahasa yang dikompilasi, variabel adalah ruang memori yang mampu menangkap nilai tipe. Dalam Python, variabel adalah nama (diambil secara internal sebagai string) terikat ke variabel referensi yang menyimpan nilai referensi ke objek target. Nama variabel adalah kunci dalam kamus internal, bagian nilai dari item kamus menyimpan nilai referensi ke target.

Nilai referensi disembunyikan dalam Python. Tidak ada tipe pengguna eksplisit untuk menyimpan nilai referensi. Namun, Anda dapat menggunakan elemen daftar (atau elemen dalam jenis wadah lain yang cocok) sebagai variabel referensi, karena semua kontainer memang menyimpan elemen juga sebagai referensi ke objek target. Dengan kata lain, elemen sebenarnya tidak terkandung di dalam wadah - hanya referensi ke elemen.

semangat
sumber
1
Sebenarnya ini dikonfirmasi dengan nilai referensi. +1 untuk jawaban ini meskipun contohnya tidak bagus.
BugShotGG
30
Menemukan terminologi baru (seperti "nilai referensi by pass" atau "call by object" tidak membantu). "Panggilan dengan (nilai | referensi | nama)" adalah istilah standar. "referensi" adalah istilah standar. Melewati referensi dengan nilai secara akurat menggambarkan perilaku Python, Java, dan sejumlah bahasa lain, menggunakan terminologi standar.
cayhorstmann
4
@cayhorstmann: Masalahnya adalah variabel Python tidak memiliki arti terminologi yang sama seperti dalam bahasa lain. Dengan cara ini, panggilan dengan referensi tidak cocok di sini. Juga, bagaimana Anda persis mendefinisikan istilah referensi ? Secara informal, cara Python dapat dengan mudah digambarkan sebagai melewatkan alamat objek. Tapi itu tidak cocok dengan implementasi Python yang berpotensi didistribusikan.
pepr
1
Saya suka jawaban ini, tetapi Anda mungkin mempertimbangkan apakah contoh itu benar-benar membantu atau melukai aliran. Juga, jika Anda mengganti 'nilai referensi' dengan 'referensi objek' Anda akan menggunakan terminologi yang dapat kita anggap 'resmi', seperti yang terlihat di sini: Mendefinisikan Fungsi
Jujur Abe
2
Ada catatan kaki yang ditunjukkan di akhir kutipan itu, yang berbunyi: "Sebenarnya, referensi panggilan dengan objek akan menjadi deskripsi yang lebih baik, karena jika objek yang bisa diubah dilewati, si penelepon akan melihat setiap perubahan yang dibuat oleh callee untuk itu ... " Saya setuju dengan Anda bahwa kebingungan disebabkan oleh mencoba menyesuaikan terminologi yang dibuat dengan bahasa lain. Selain semantik, hal-hal yang perlu dipahami adalah: kamus / ruang nama , operasi pengikatan nama dan hubungan nama → pointer → objek (seperti yang sudah Anda ketahui).
Jujur Abe
56

Tidak ada variabel dalam Python

Kunci untuk memahami passing parameter adalah berhenti memikirkan "variabel". Ada nama dan objek dalam Python dan bersama-sama mereka muncul seperti variabel, tetapi berguna untuk selalu membedakan ketiganya.

  1. Python memiliki nama dan objek.
  2. Tugas mengikat nama ke suatu objek.
  3. Melewati argumen ke fungsi juga mengikat nama (nama parameter fungsi) ke objek.

Hanya itu yang ada untuk itu. Mutabilitas tidak relevan dengan pertanyaan ini.

Contoh:

a = 1

Ini mengikat nama ake objek tipe integer yang memegang nilai 1.

b = x

Ini mengikat nama bke objek yang sama dengan nama xyang terikat saat ini. Setelah itu, nama bitu tidak ada hubungannya dengan nama itu xlagi.

Lihat bagian 3.1 dan 4.2 dalam referensi bahasa Python 3.

Cara membaca contoh di pertanyaan

Dalam kode yang ditunjukkan dalam pertanyaan, pernyataan self.Change(self.variable)mengikat nama var(dalam lingkup fungsi Change) ke objek yang menyimpan nilai 'Original'dan tugas var = 'Changed'(dalam tubuh fungsi Change) menetapkan nama yang sama lagi: ke beberapa objek lain (yang terjadi untuk memegang string juga tetapi bisa menjadi sesuatu yang lain sama sekali).

Cara melewati referensi

Jadi jika hal yang ingin Anda ubah adalah objek yang bisa berubah, tidak ada masalah, karena semuanya secara efektif dilewatkan oleh referensi.

Jika itu adalah objek yang tidak dapat diubah (misalnya bool, angka, string), cara untuk pergi adalah membungkusnya dengan objek yang bisa berubah.
Solusi cepat dan kotor untuk ini adalah daftar satu elemen (alih-alih self.variable, lulus [self.variable]dan dalam fungsi modifikasi var[0]).
Pendekatan yang lebih pythonic adalah dengan memperkenalkan kelas satu atribut yang sepele. Fungsi menerima instance kelas dan memanipulasi atribut.

Lutz Prechelt
sumber
20
"Python tidak memiliki variabel" adalah slogan konyol dan membingungkan, dan saya benar-benar berharap orang-orang akan berhenti mengatakannya ... :( Sisa dari jawaban ini baik!
Ned Batchelder
9
Ini mungkin mengejutkan, tetapi tidak konyol. Dan saya juga tidak berpikir itu membingungkan: Mudah-mudahan itu membuka pikiran penerima untuk penjelasan yang akan datang dan menempatkannya dalam sikap "Saya ingin tahu apa yang mereka miliki alih-alih variabel". (Ya, jarak tempuh Anda mungkin beragam.)
Lutz Prechelt
13
apakah Anda juga akan mengatakan bahwa Javascript tidak memiliki variabel? Mereka bekerja sama dengan Python. Juga, Java, Ruby, PHP, .... Saya pikir teknik pengajaran yang lebih baik adalah, "variabel Python bekerja berbeda dari C."
Ned Batchelder
9
Ya, Java memiliki variabel. Begitu juga dengan Python, dan JavaScript, Ruby, PHP, dll. Anda tidak akan mengatakan dalam Java yang intmendeklarasikan variabel, tetapi Integertidak. Keduanya mendeklarasikan variabel. The Integervariabel adalah obyek, yang intvariabel adalah primitif. Sebagai contoh, Anda menunjukkan bagaimana variabel Anda bekerja dengan menunjukkan a = 1; b = a; a++ # doesn't modify b. Itu persis benar di Python juga (menggunakan += 1karena tidak ada ++di Python)!
Ned Batchelder
1
Konsep "variabel" itu kompleks dan sering tidak jelas: Suatu variabel adalah wadah untuk suatu nilai, diidentifikasi dengan sebuah nama. Dalam Python, nilainya adalah objek, wadah adalah objek (lihat masalahnya?) Dan nama-nama sebenarnya adalah hal-hal yang terpisah. Saya percaya jauh lebih sulit untuk mendapatkan pemahaman yang akurat tentang variabel dengan cara ini. Penjelasan nama-dan-objek tampak lebih sulit, tetapi sebenarnya lebih sederhana.
Lutz Prechelt
43

Trik sederhana yang biasa saya gunakan adalah dengan membungkusnya dalam daftar:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Ya saya tahu ini bisa merepotkan, tapi kadang-kadang cukup sederhana untuk melakukan ini.)

AmanicA
sumber
7
+1 untuk sejumlah kecil teks yang memberikan solusi penting untuk masalah Python yang tidak memiliki pass-by-reference. (Sebagai komentar tindak lanjut / pertanyaan yang cocok di sini dan di mana pun di halaman ini: Tidak jelas bagi saya mengapa python tidak dapat memberikan kata kunci "ref" seperti C #, yang hanya membungkus argumen pemanggil dalam daftar seperti ini, dan memperlakukan referensi ke argumen dalam fungsi sebagai elemen ke-0 dari daftar.)
M Katz
5
Bagus. Untuk melewati referensi, masukkan [].
Justas
38

(sunting - Blair telah memperbarui jawabannya yang sangat populer sehingga kini akurat)

Saya pikir penting untuk dicatat bahwa posting saat ini dengan suara terbanyak (oleh Blair Conrad), meskipun benar sehubungan dengan hasilnya, menyesatkan dan garis batas tidak benar berdasarkan definisi. Meskipun ada banyak bahasa (seperti C) yang memungkinkan pengguna untuk melewati referensi atau lewat nilai, Python bukan salah satunya.

Jawaban David Cournapeau menunjuk ke jawaban yang sebenarnya dan menjelaskan mengapa perilaku di pos Blair Conrad tampaknya benar sedangkan definisi tidak.

Sejauh Python dinyatakan sebagai nilai, semua bahasa lulus nilai karena sebagian data (baik itu "nilai" atau "referensi") harus dikirim. Namun, itu tidak berarti bahwa Python dilewati oleh nilai dalam arti bahwa seorang programmer C akan memikirkannya.

Jika Anda menginginkan perilakunya, jawaban Blair Conrad baik-baik saja. Tetapi jika Anda ingin mengetahui mur dan baut mengapa Python tidak lulus nilai atau lulus referensi, baca jawaban David Cournapeau.

KobeJohn
sumber
4
Tidak benar bahwa semua bahasa disebut berdasarkan nilai. Dalam C ++ atau Pascal (dan tentu saja banyak orang lain yang saya tidak tahu), Anda memiliki panggilan dengan referensi. Misalnya, dalam C ++, void swap(int& x, int& y) { int temp = x; x = y; y = temp; }akan menukar variabel yang diteruskan ke sana. Di Pascal, Anda menggunakan varbukan &.
cayhorstmann
2
Saya pikir saya telah menjawab ini sejak lama tetapi saya tidak melihatnya. Untuk kelengkapan - cayhorstmann salah paham jawaban saya. Saya tidak mengatakan semuanya adalah panggilan dengan nilai dalam istilah yang kebanyakan orang pelajari pertama kali tentang C / C ++ . Itu hanya karena beberapa nilai dilewatkan (nilai, nama, pointer, dll.) Dan bahwa istilah yang digunakan dalam jawaban asli Blair tidak akurat.
KobeJohn
24

Anda mendapat jawaban yang sangat bagus di sini.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )
bobobobo
sumber
2
ya, namun jika Anda melakukannya x = [2, 4, 4, 5, 5], y = x, X [0] = 1, cetak x # [1, 4, 4, 5, 5] cetak y # [1 , 4, 4, 5, 5]
laycat
19

Dalam hal ini variabel yang berjudul vardalam metode Changeini diberikan referensi self.variable, dan Anda segera menetapkan string var. Tidak lagi menunjuk ke self.variable. Cuplikan kode berikut menunjukkan apa yang akan terjadi jika Anda memodifikasi struktur data yang ditunjukkan oleh vardan self.variable, dalam hal ini daftar:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

Saya yakin orang lain bisa mengklarifikasi ini lebih lanjut.

Mike Mazur
sumber
18

Skema pass-by-assignment Python tidak persis sama dengan opsi parameter referensi C ++, tetapi ternyata sangat mirip dengan model passing-argumen dari bahasa C (dan lainnya) dalam praktiknya:

  • Argumen yang tidak dapat diubah secara efektif disahkan “ berdasarkan nilai ”. Objek seperti bilangan bulat dan string dilewatkan dengan referensi objek alih-alih dengan menyalin, tetapi karena Anda tidak dapat mengubah objek tetap di tempat bagaimanapun, efeknya seperti membuat salinan.
  • Argumen yang bisa diubah secara efektif disahkan " oleh pointer ." Objek seperti daftar dan kamus juga dilewatkan oleh referensi objek, yang mirip dengan cara C melewati array sebagai pointer — objek yang dapat berubah dapat diubah di tempat dalam fungsi, seperti array C.
ajknzhol
sumber
16

Seperti yang bisa Anda nyatakan, Anda perlu memiliki objek yang bisa berubah, tetapi izinkan saya menyarankan Anda untuk memeriksa variabel global karena mereka dapat membantu Anda atau bahkan memecahkan masalah seperti ini!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

contoh:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
Nuno Aniceto
sumber
5
Saya tergoda untuk mengirim tanggapan yang serupa - si penanya awal mungkin tidak tahu bahwa yang dia inginkan sebenarnya adalah menggunakan variabel global, dibagi di antara berbagai fungsi. Berikut tautan yang akan saya bagikan: stackoverflow.com/questions/423379/... Sebagai jawaban untuk @Tim, Stack Overflow bukan hanya situs tanya jawab, ini adalah gudang luas referensi pengetahuan yang hanya menjadi lebih kuat dan lebih bernuansa - banyak seperti wiki- aktif dengan lebih banyak input.
Max P Magee
13

Banyak wawasan dalam jawaban di sini, tetapi saya pikir poin tambahan tidak secara jelas disebutkan di sini secara eksplisit. Mengutip dari dokumentasi python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

"Dalam Python, variabel yang hanya direferensikan di dalam suatu fungsi secara implisit bersifat global. Jika suatu variabel diberi nilai baru di mana saja di dalam tubuh fungsi, itu diasumsikan sebagai lokal. Jika suatu variabel pernah diberi nilai baru di dalam fungsi, variabelnya implisit lokal, dan Anda perlu secara eksplisit mendeklarasikannya sebagai 'global'. Meskipun sedikit mengejutkan pada awalnya, pertimbangan sesaat menjelaskan hal ini. Di satu sisi, memerlukan global untuk variabel-variabel yang ditugaskan menyediakan bilah terhadap efek samping yang tidak diinginkan. sebaliknya, jika global diperlukan untuk semua referensi global, Anda akan menggunakan global setiap saat. Anda harus mendeklarasikan sebagai global setiap referensi ke fungsi bawaan atau ke komponen modul yang diimpor. akan mengalahkan kegunaan deklarasi global untuk mengidentifikasi efek samping. "

Bahkan ketika meneruskan objek yang dapat diubah ke suatu fungsi, ini masih berlaku. Dan bagi saya jelas menjelaskan alasan perbedaan perilaku antara menetapkan ke objek dan beroperasi pada objek dalam fungsi.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

memberi:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

Oleh karena itu penugasan ke variabel global yang tidak dinyatakan global membuat objek lokal baru dan memutus tautan ke objek asli.

Joop
sumber
10

Inilah penjelasan sederhana (saya harap) dari konsep yang pass by objectdigunakan dalam Python.
Setiap kali Anda melewati objek ke fungsi, objek itu sendiri dilewatkan (objek dengan Python sebenarnya yang Anda sebut nilai dalam bahasa pemrograman lain) bukan referensi ke objek ini. Dengan kata lain, saat Anda menelepon:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

Objek aktual - [0, 1] (yang akan disebut nilai dalam bahasa pemrograman lain) sedang diteruskan. Jadi sebenarnya fungsi change_meakan mencoba melakukan sesuatu seperti:

[0, 1] = [1, 2, 3]

yang jelas tidak akan mengubah objek yang dilewatkan ke fungsi. Jika fungsinya tampak seperti ini:

def change_me(list):
   list.append(2)

Maka panggilan akan menghasilkan:

[0, 1].append(2)

yang jelas akan mengubah objek. Jawaban ini menjelaskannya dengan baik.

matino
sumber
2
Masalahnya adalah tugas itu melakukan sesuatu yang lain dari yang Anda harapkan. The list = [1, 2, 3]penyebab menggunakan kembali listnama untuk sesuatu yang lain dan forgeting objek awalnya berlalu. Namun, Anda dapat mencoba list[:] = [1, 2, 3](omong-omong listadalah nama yang salah untuk variabel. Memikirkan [0, 1] = [1, 2, 3]itu omong kosong. Pokoknya, menurut Anda apa artinya objek itu dilewatkan ? Apa yang disalin ke fungsi menurut pendapat Anda?
pepr
Objek @pepr bukan literal. Mereka adalah benda. Satu-satunya cara untuk berbicara tentang mereka adalah memberi mereka beberapa nama. Itu sebabnya begitu sederhana setelah Anda pegang, tetapi sangat rumit untuk dijelaskan. :-)
Veky
@Veky: Saya tahu itu. Lagi pula, daftar literal dikonversi ke objek daftar. Sebenarnya, objek apa pun di Python bisa ada tanpa nama, dan itu bisa digunakan bahkan ketika tidak diberi nama apa pun. Dan Anda dapat menganggapnya sebagai benda anonim. Pikirkan tentang objek yang menjadi elemen daftar. Mereka tidak perlu nama. Anda dapat mengaksesnya melalui pengindeksan atau iterasi melalui daftar. Bagaimanapun, saya bersikeras [0, 1] = [1, 2, 3]hanyalah contoh yang buruk. Tidak ada yang seperti itu di Python.
Pepr
@pepr: Saya tidak bermaksud menyebutkan nama definisi-Python, hanya nama biasa. Tentu saja alist[2]dianggap sebagai nama elemen alist ketiga. Tapi saya pikir saya salah mengerti apa masalah Anda. :-)
Veky
Argh. Bahasa Inggris saya jelas jauh lebih buruk daripada Python saya. :-) Saya akan mencoba sekali lagi. Saya baru saja mengatakan Anda harus memberi objek beberapa nama hanya untuk membicarakannya. Dengan "nama" yang saya maksud bukan "nama yang didefinisikan oleh Python". Saya tahu mekanisme Python, jangan khawatir.
Veky
8

Selain semua penjelasan hebat tentang cara kerja hal ini dalam Python, saya tidak melihat saran sederhana untuk masalah ini. Saat Anda tampaknya membuat objek dan instance, cara pythonic menangani variabel instance dan mengubahnya adalah sebagai berikut:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

Dalam metode instan, Anda biasanya merujuk selfke akses atribut instan. Adalah normal untuk mengatur atribut instance __init__dan membaca atau mengubahnya dalam metode instance. Itu juga mengapa Anda memberikan selfargumen pertama def Change.

Solusi lain adalah membuat metode statis seperti ini:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var
Dolf Andringa
sumber
6

Ada sedikit trik untuk melewatkan objek dengan referensi, meskipun bahasa tidak memungkinkan. Ini berfungsi di Jawa juga, itu daftar dengan satu item. ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

Ini hack yang jelek, tapi berhasil. ;-P

itmuckel
sumber
padalah referensi ke objek daftar bisa berubah yang pada gilirannya menyimpan objek obj. Referensi 'p', diteruskan ke changeRef. Di dalam changeRef, referensi baru dibuat (referensi baru disebut ref) yang menunjuk ke objek daftar yang sama yang pmenunjuk. Tetapi karena daftar bisa berubah, perubahan daftar dapat dilihat oleh kedua referensi. Dalam hal ini, Anda menggunakan refreferensi untuk mengubah objek pada indeks 0 sehingga selanjutnya menyimpan PassByReference('Michael')objek. Perubahan ke objek daftar dilakukan menggunakan reftetapi perubahan ini terlihat oleh p.
Minh Tran
Jadi sekarang, referensi pdan refarahkan ke daftar objek yang menyimpan objek tunggal PassByReference('Michael'),. Jadi itu p[0].namekembali Michael. Tentu saja, refsekarang telah keluar dari ruang lingkup dan mungkin sampah yang dikumpulkan tetapi semuanya sama.
Minh Tran
Anda belum mengubah variabel instance pribadi name, dari PassByReferenceobjek asli yang terkait dengan referensi obj. Bahkan obj.nameakan kembali Peter. Komentar tersebut mengasumsikan definisi yang Mark Ransomdiberikan.
Minh Tran
Intinya, saya tidak setuju bahwa ini adalah peretasan (yang saya maksud untuk merujuk pada sesuatu yang berfungsi tetapi untuk alasan yang tidak diketahui, tidak diuji, atau tidak diinginkan oleh pelaksana). Anda cukup mengganti satu PassByReferenceobjek dengan objek lain PassByReferencedi daftar Anda dan merujuk ke yang terakhir dari dua objek.
Minh Tran
5

Saya menggunakan metode berikut untuk dengan cepat mengkonversi beberapa kode Fortran ke Python. Benar, ini tidak lulus sebagai referensi ketika pertanyaan asli diajukan, tetapi merupakan pekerjaan sederhana dalam beberapa kasus.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c
Brad Porter
sumber
Ya, ini memecahkan 'pass by reference' dalam use case saya juga. Saya memiliki fungsi yang pada dasarnya membersihkan nilai dalam dictdan kemudian mengembalikan dict. Namun, saat membersihkannya mungkin diperlukan pembangunan kembali bagian dari sistem. Oleh karena itu, fungsi tidak hanya mengembalikan yang dibersihkan dicttetapi juga dapat memberi sinyal pembangunan kembali. Saya mencoba untuk melewati boolreferensi, tetapi ofc itu tidak berhasil. Mencari tahu bagaimana menyelesaikan ini, saya menemukan solusi Anda (pada dasarnya mengembalikan tuple) untuk bekerja dengan baik sementara juga tidak menjadi hack / solusi sama sekali (IMHO).
kasimir
3

Meskipun referensi lewat tidak ada yang cocok dengan python dan harus jarang digunakan ada beberapa solusi yang benar-benar dapat bekerja untuk mendapatkan objek yang saat ini ditugaskan ke variabel lokal atau bahkan menugaskan kembali variabel lokal dari dalam fungsi yang disebut.

Ide dasarnya adalah memiliki fungsi yang dapat melakukan akses itu dan dapat dilewatkan sebagai objek ke fungsi lain atau disimpan di kelas.

Salah satu caranya adalah dengan menggunakan global(untuk variabel global) atau nonlocal(untuk variabel lokal dalam suatu fungsi) dalam fungsi wrapper.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

Gagasan yang sama berfungsi untuk membaca dan delmembuat variabel.

Untuk hanya membaca bahkan ada cara yang lebih singkat dari hanya menggunakan lambda: xyang mengembalikan callable yang ketika dipanggil mengembalikan nilai x saat ini. Ini agak seperti "panggilan dengan nama" yang digunakan dalam bahasa di masa lalu.

Melewati 3 pembungkus untuk mengakses sebuah variabel agak sulit sehingga mereka dapat dibungkus ke dalam kelas yang memiliki atribut proxy:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Dukungan "pantulan" ular sanca memungkinkan untuk mendapatkan objek yang mampu menetapkan kembali nama / variabel dalam lingkup yang diberikan tanpa mendefinisikan fungsi secara eksplisit dalam lingkup itu:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Di sini ByRefkelas membungkus akses kamus. Jadi akses atribut wrappedditerjemahkan ke akses item dalam kamus yang dikirimkan. Dengan melewatkan hasil dari builtin localsdan nama variabel lokal ini akhirnya mengakses variabel lokal. Dokumentasi python pada 3.5 menyarankan bahwa mengubah kamus mungkin tidak berfungsi tetapi tampaknya bekerja untuk saya.

textshell
sumber
3

mengingat cara python menangani nilai dan referensi padanya, satu-satunya cara Anda dapat mereferensikan atribut instance arbitrary adalah dengan nama:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

dalam kode nyata Anda tentu saja akan menambahkan pengecekan error pada pencarian dict.

MARK bLOORE
sumber
3

Karena kamus dilewatkan dengan referensi, Anda dapat menggunakan variabel dict untuk menyimpan nilai yang dirujuk di dalamnya.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value
Liakos
sumber
3

Pass-By-Reference dalam Python sangat berbeda dari konsep pass by reference di C ++ / Java.

  • Java & C #: tipe primitif (termasuk string) lewat nilai (salin), tipe referensi dilewatkan oleh referensi (salinan alamat) sehingga semua perubahan yang dibuat dalam parameter dalam fungsi yang dipanggil terlihat oleh pemanggil.
  • C ++: Baik pass-by-reference atau pass-by-value diizinkan. Jika suatu parameter dilewatkan dengan referensi, Anda dapat memodifikasinya atau tidak tergantung pada apakah parameter itu dilewatkan sebagai konst atau tidak. Namun, const atau tidak, parameter mempertahankan referensi ke objek dan referensi tidak dapat ditugaskan untuk menunjuk ke objek yang berbeda dalam fungsi yang dipanggil.
  • Python: Python adalah "pass-by-object-reference", yang sering dikatakan: "Referensi objek dilewatkan oleh nilai." [Baca di sini] 1. Baik pemanggil dan fungsi merujuk ke objek yang sama tetapi parameter dalam fungsi adalah variabel baru yang hanya memegang salinan objek di pemanggil. Seperti C ++, parameter dapat dimodifikasi atau tidak berfungsi - Ini tergantung pada jenis objek yang dilewati. misalnya; Jenis objek yang tidak dapat diubah tidak dapat dimodifikasi dalam fungsi yang dipanggil sedangkan objek yang dapat diubah dapat diperbarui atau diinisialisasi ulang. Perbedaan penting antara memperbarui atau menetapkan ulang / menginisialisasi ulang variabel yang dapat berubah adalah bahwa nilai yang diperbarui tercermin kembali dalam fungsi yang dipanggil sedangkan nilai yang diinisialisasi ulang tidak. Cakupan penugasan objek baru ke variabel yang bisa berubah adalah lokal ke fungsi dalam python. Contoh yang diberikan oleh @ blair-conrad sangat bagus untuk memahami hal ini.
Alok Garg
sumber
1
Tua tapi saya merasa berkewajiban untuk memperbaikinya. String dilewatkan dengan referensi dalam Java dan C #, BUKAN oleh nilai
John
2

Karena contoh Anda berorientasi objek, Anda dapat membuat perubahan berikut untuk mencapai hasil yang serupa:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'
Jesse Hogan
sumber
1
Meskipun ini berhasil. Itu tidak lulus dengan referensi. Ini adalah 'lewat referensi objek'.
Bishwas Mishra
1

Anda hanya dapat menggunakan kelas kosong sebagai instance untuk menyimpan objek referensi karena atribut objek internal disimpan dalam kamus instance. Lihat contohnya.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
sergzach
sumber
1

Karena tampaknya tidak disebutkan pendekatan untuk mensimulasikan referensi seperti diketahui dari misalnya C ++ adalah dengan menggunakan fungsi "pembaruan" dan meneruskannya alih-alih variabel aktual (atau lebih tepatnya, "nama"):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

Ini sebagian besar berguna untuk "referensi saja" atau dalam situasi dengan beberapa utas / proses (dengan membuat utas fungsi pembaruan / multiprosesing aman).

Jelas di atas tidak memungkinkan membaca nilai, hanya memperbaruinya.

Daniel Jour
sumber