Saya mencoba menerapkan closure dengan Python 2.6 dan saya perlu mengakses variabel nonlokal tetapi sepertinya kata kunci ini tidak tersedia di python 2.x. Bagaimana cara mengakses variabel nonlokal dalam closure di versi python ini?
117
def inner(): print d; d = {'y': 1}
. Di sini,print d
membaca di luard
sehingga menciptakan variabel nonlokald
dalam lingkup dalam.X = 1
hanya mengikat namaX
dengan objek tertentu (int
dengan nilai1
).X = 1; Y = X
mengikat dua nama ke objek yang sama persis. Bagaimanapun, beberapa objek dapat berubah dan Anda dapat mengubah nilainya.Solusi berikut ini terinspirasi oleh jawaban Elias Zamaria , tetapi berlawanan dengan jawaban tersebut, jawaban tersebut menangani banyak panggilan dari fungsi luar dengan benar. "Variabel"
inner.y
bersifat lokal untuk panggilan saat iniouter
. Hanya itu bukan variabel, karena itu dilarang, tetapi atribut objek (objek adalah fungsinyainner
sendiri). Ini sangat jelek (perhatikan bahwa atribut hanya dapat dibuat setelahinner
fungsi ditentukan) tetapi tampaknya efektif.sumber
inc()
dandec()
kembali dari luar kenaikan dan penurunan itu sebagai penghitung bersama. Kemudian Anda harus memutuskan fungsi mana untuk melampirkan nilai counter saat ini dan mereferensikan fungsi itu dari yang lain. Yang terlihat agak aneh dan asimetris. Misal didec()
baris sepertiinc.value -= 1
.Daripada kamus, ada lebih sedikit kekacauan di kelas nonlokal . Mengubah contoh @ ChrisB :
Kemudian
Setiap panggilan outer () membuat kelas baru dan berbeda yang disebut konteks (bukan hanya instance baru). Jadi ini menghindari kewaspadaan @ Nathaniel tentang konteks bersama.
sumber
__slots__ = ()
dan membuat sebuah objek daripada menggunakan kelas, misalnyacontext.z = 3
akan memunculkan sebuahAttributeError
. Hal ini dimungkinkan untuk semua kelas, kecuali mereka mewarisi dari kelas yang tidak menentukan slot.Saya pikir kuncinya di sini adalah apa yang Anda maksud dengan "akses". Seharusnya tidak ada masalah dengan membaca variabel di luar lingkup closure, misalnya,
harus bekerja seperti yang diharapkan (pencetakan 3). Namun, menimpa nilai x tidak akan berhasil, misalnya,
akan tetap mencetak 3. Dari pemahaman saya tentang PEP-3104, inilah yang dimaksud dengan kata kunci nonlokal. Seperti yang disebutkan di PEP, Anda dapat menggunakan kelas untuk mencapai hal yang sama (agak berantakan):
sumber
def ns(): pass
diikuti olehns.x = 3
. Itu tidak cantik, tapi sedikit kurang jelek di mataku.class Namespace: x = 3
?ns
adalah objek global yang mengapa Anda dapat merujukns.x
pada level modul dalamprint
pernyataan di bagian paling akhir .Ada cara lain untuk mengimplementasikan variabel nonlokal dengan Python 2, jika ada jawaban di sini yang tidak diinginkan karena alasan apa pun:
Tidak ada gunanya menggunakan nama fungsi dalam pernyataan penugasan variabel, tetapi terlihat lebih sederhana dan lebih bersih daripada meletakkan variabel dalam kamus. Nilainya diingat dari satu panggilan ke panggilan lainnya, seperti dalam jawaban Chris B.
sumber
f = outer()
dan kemudian melakukannyag = outer()
, makaf
penghitung akan disetel ulang. Ini karena keduanya berbagi variabel tunggalouter.y
, bukan masing-masing memiliki variabel independen sendiri. Meskipun kode ini terlihat lebih estetis daripada jawaban Chris B, caranya tampaknya menjadi satu-satunya cara untuk meniru pelingkupan leksikal jika Anda ingin meneleponouter
lebih dari sekali.outer.y
tidak melibatkan apa pun yang bersifat lokal ke pemanggilan fungsi (instance)outer()
, tetapi menetapkan ke atribut objek fungsi yang terikat ke namaouter
dalam cakupan yang melingkupinya . Dan oleh karena itu seseorang dapat juga menggunakan, secara tertulisouter.y
, nama lain selainouter
, asalkan diketahui terikat dalam ruang lingkup itu. Apakah ini benar?outer.y
menggunakan namainner.y
(karenainner
terikat di dalam panggilanouter()
, yang merupakan cakupan yang kita inginkan), tetapi meletakkan inisialisasiinner.y = 0
setelah definisi inner (sebagai objek harus ada saat atributnya dibuat), tapi tentu saja sebelumnyareturn inner
?Berikut adalah sesuatu yang terinspirasi dari saran Alois Mahdal dalam komentarnya tentang jawaban lain :
Memperbarui
Setelah melihat kembali hal ini baru-baru ini, saya terkejut dengan bagaimana dekorator-seperti itu — ketika saya sadar bahwa menerapkannya sebagai seseorang akan membuatnya lebih umum & berguna (meskipun melakukannya bisa dibilang menurunkan keterbacaannya sampai taraf tertentu).
Perhatikan bahwa kedua versi bekerja dengan Python 2 dan 3.
sumber
Ada kutil dalam aturan pelingkupan python - tugas membuat variabel lokal ke lingkup fungsi yang langsung melingkupinya. Untuk variabel global, Anda akan menyelesaikannya dengan
global
kata kunci.Solusinya adalah dengan memperkenalkan objek yang dibagi di antara dua cakupan, yang berisi variabel yang bisa berubah, tetapi itu sendiri direferensikan melalui variabel yang tidak ditetapkan.
Alternatifnya adalah beberapa peretasan cakupan:
Anda mungkin bisa menemukan beberapa trik untuk mendapatkan nama parameter
outer
, dan kemudian meneruskannya sebagai varname, tetapi tanpa bergantung pada namaouter
Anda ingin menggunakan kombinator Y.sumber
nonlocal
.locals()
membuat kamus dari orangouter()
lokal pada saatinner()
itu ditentukan tetapi mengubah kamus itu tidak mengubahv
dalamouter()
. Ini tidak akan berfungsi lagi ketika Anda memiliki lebih banyak fungsi dalam yang ingin berbagi variabel tertutup. Ucapkan ainc()
dandec()
itu kenaikan dan penurunan penghitung bersama.nonlocal
adalah fitur python 3.nonlocal
di Python 2 secara umum . Ide Anda tidak mencakup kasus umum tetapi hanya satu dengan satu fungsi batin. Lihat inti ini sebagai contoh. Kedua fungsi dalam tersebut memiliki wadahnya masing-masing. Anda memerlukan objek yang bisa berubah dalam lingkup fungsi luar, seperti jawaban lain yang sudah disarankan.nonlocal
kata kunci yang diperkenalkan di Python 3.Cara lain untuk melakukannya (meskipun terlalu bertele-tele):
sumber
Memperluas solusi elegan Martineau di atas menjadi kasus penggunaan yang praktis dan agak kurang elegan, saya dapatkan:
sumber
Gunakan variabel global
Secara pribadi, saya tidak suka variabel global. Tapi, proposal saya didasarkan pada https://stackoverflow.com/a/19877437/1083704 jawaban
di mana pengguna perlu mendeklarasikan variabel global
ranks
, setiap kali Anda perlu memanggilreport
. Peningkatan saya menghilangkan kebutuhan untuk menginisialisasi variabel fungsi dari pengguna.sumber
inner
, tetapi tidak dapat menetapkannya, tetapi Anda dapat mengubah kunci dan nilainya. Ini menghindari penggunaan variabel global.