Saya telah melihat dan menggunakan fungsi bersarang di Python, dan mereka cocok dengan definisi penutupan. Jadi mengapa mereka disebut nested functions
bukan closures
?
Apakah fungsi bersarang tidak tertutup karena tidak digunakan oleh dunia luar?
UPDATE: Saya membaca tentang penutupan dan membuat saya berpikir tentang konsep ini sehubungan dengan Python. Saya mencari dan menemukan artikel yang disebutkan oleh seseorang dalam komentar di bawah, tetapi saya tidak bisa sepenuhnya memahami penjelasan dalam artikel itu, jadi itu sebabnya saya menanyakan pertanyaan ini.
python
closures
nested-function
Srikar Appalaraju
sumber
sumber
Jawaban:
Penutupan terjadi ketika suatu fungsi memiliki akses ke variabel lokal dari lingkup terlampir yang telah menyelesaikan eksekusi.
Ketika
make_printer
dipanggil, frame baru diletakkan di stack dengan kode yang dikompilasi untukprinter
fungsi sebagai konstanta dan nilaimsg
sebagai lokal. Itu kemudian membuat dan mengembalikan fungsi. Karena fungsiprinter
referensimsg
variabel, itu tetap hidup setelahmake_printer
fungsi telah kembali.Jadi, jika fungsi bersarang Anda tidak
maka mereka bukan penutupan.
Berikut adalah contoh fungsi bersarang yang bukan merupakan penutupan.
Di sini, kami mengikat nilai ke nilai default parameter. Ini terjadi ketika fungsi
printer
dibuat dan jadi tidak ada referensi ke nilaimsg
eksternalprinter
perlu dipertahankan setelahmake_printer
kembali.msg
hanya variabel lokal normal dari fungsiprinter
dalam konteks ini.sumber
self
. (Dalam JavaScript / Python itu hampir benar.)i
] dari lingkup melampirkan". merujuk, yaitu dapat memeriksa (atau mengubah)i
nilai, bahkan jika / ketika lingkup itu "telah menyelesaikan eksekusi", yaitu pelaksanaan suatu program telah maju ke bagian lain dari kode. Bloki
yang didefinisikan tidak lebih, namun fungsi yang merujuki
masih dapat melakukannya. Ini biasanya digambarkan sebagai "penutupan variabeli
". Untuk tidak berurusan dengan variabel spesifik, itu dapat diimplementasikan sebagai penutupan seluruh kerangka lingkungan di mana variabel itu didefinisikan.Pertanyaannya sudah dijawab oleh aaronasterling
Namun, seseorang mungkin tertarik pada bagaimana variabel disimpan di bawah tenda.
Sebelum datang ke cuplikan:
Penutupan adalah fungsi yang mewarisi variabel dari lingkungan terlampirnya. Ketika Anda melewatkan fungsi panggilan balik sebagai argumen ke fungsi lain yang akan melakukan I / O, fungsi panggilan balik ini akan dipanggil nanti, dan fungsi ini akan - hampir secara ajaib - mengingat konteks di mana ia dinyatakan, bersama dengan semua variabel yang tersedia dalam konteks itu.
Jika suatu fungsi tidak menggunakan variabel bebas itu tidak membentuk penutupan.
Jika ada level dalam lain yang menggunakan variabel bebas - semua level sebelumnya menyimpan lingkungan leksikal (contoh di akhir)
atribut fungsi
func_closure
dalam python <3.X atau__closure__
dalam python> 3.X menyimpan variabel gratis.Setiap fungsi dalam python memiliki atribut penutupan ini, tetapi tidak menyimpan konten apa pun jika tidak ada variabel gratis.
contoh: atribut penutupan tetapi tidak ada konten di dalamnya karena tidak ada variabel gratis.
NB: VARIABEL GRATIS HARUS MENCIPTAKAN PENUTUPAN.
Saya akan menjelaskan menggunakan cuplikan yang sama seperti di atas:
Dan semua fungsi Python memiliki atribut closure jadi mari kita periksa variabel-variabel penutup yang terkait dengan fungsi closure.
Berikut ini adalah atribut
func_closure
untuk fungsi tersebutprinter
The
closure
atribut mengembalikan tuple benda sel yang berisi rincian dari variabel didefinisikan dalam lingkup melampirkan.Elemen pertama dalam func_closure yang bisa berupa None atau tuple sel yang berisi binding untuk variabel bebas fungsi dan hanya baca.
Di sini, di output di atas Anda dapat melihat
cell_contents
, mari kita lihat apa yang disimpannya:Jadi, ketika kita memanggil fungsi
printer()
, ia mengakses nilai yang disimpan di dalamcell_contents
. Beginilah cara kami mendapatkan output sebagai 'Foo!'Sekali lagi saya akan menjelaskan menggunakan cuplikan di atas dengan beberapa perubahan:
Dalam cuplikan di atas, saya tidak mencetak pesan di dalam fungsi printer, sehingga tidak membuat variabel gratis. Karena tidak ada variabel gratis, tidak akan ada konten di dalam penutupan. Itulah tepatnya yang kita lihat di atas.
Sekarang saya akan menjelaskan cuplikan lain untuk membersihkan semuanya
Free Variable
denganClosure
:Jadi, kita melihat bahwa
func_closure
properti adalah kumpulan sel penutupan , kita dapat merujuknya dan isinya secara eksplisit - sel memiliki properti "cell_contents"Di sini ketika kita menelepon
inn
, itu akan merujuk semua variabel bebas simpan sehingga kita dapatkanI am free variable
sumber
func_closure
sekarang disebut__closure__
, mirip dengan berbagaifunc_*
atribut lainnya .__closure_
tersedia dalam Python 2.6+ untuk kompatibilitas dengan Python 3.__closure__
objek yang merupakan penutupan.Python memiliki dukungan yang lemah untuk penutupan. Untuk melihat apa yang saya maksud, ambil contoh berikut dari penghitung yang menggunakan penutupan dengan JavaScript:
Penutupan cukup elegan karena memberikan fungsi yang ditulis seperti ini kemampuan untuk memiliki "memori internal". Pada Python 2.7 ini tidak mungkin. Jika kamu mencoba
Anda akan mendapatkan kesalahan dengan mengatakan bahwa x tidak didefinisikan. Tetapi bagaimana itu bisa terjadi jika telah ditunjukkan oleh orang lain bahwa Anda dapat mencetaknya? Ini karena bagaimana Python mengelola ruang lingkup fungsi variabel. Sementara fungsi dalam dapat membaca variabel fungsi luar, ia tidak dapat menuliskannya .
Ini sungguh memalukan. Tetapi dengan hanya read-only closure Anda setidaknya dapat menerapkan pola dekorator fungsi yang Python menawarkan gula sintaksis.
Memperbarui
Seperti yang telah ditunjukkan, ada beberapa cara untuk mengatasi batasan lingkup python dan saya akan mengungkap beberapa.
1. Gunakan
global
kata kunci (secara umum tidak disarankan).2. Dalam Python 3.x, gunakan
nonlocal
kata kunci (disarankan oleh @unutbu dan @leewz)3. Tentukan kelas modifikasi sederhana
Object
dan membuat bagian
Object scope
dalaminitCounter
untuk menyimpan variabelKarena
scope
benar-benar hanya referensi, tindakan yang diambil dengan bidangnya tidak benar-benar mengubahscope
dirinya sendiri, sehingga tidak ada kesalahan muncul.4. Cara alternatif, seperti yang ditunjukkan @unutbu, adalah mendefinisikan setiap variabel sebagai array (
x = [0]
) dan memodifikasi elemen pertama (x[0] += 1
). Sekali lagi tidak ada kesalahan muncul karenax
itu sendiri tidak dimodifikasi.5. Seperti yang disarankan oleh @raxacoricofallapatorius, Anda dapat membuat
x
properticounter
sumber
x = [0]
di lingkup luar, dan menggunakannyax[0] += 1
di lingkup dalam. Di Python3, Anda dapat menyimpan kode seperti apa adanya dan menggunakan kata kunci nonlokal .x
ditunjuk variabel tetap sama persis meskipun Anda meneleponinc()
atau apa pun, dan Anda tidak secara efektif menulis ke variabel.x
properticounter
.nonlocal
kata kunci, yang sepertiglobal
tetapi untuk variabel fungsi luar. Ini akan memungkinkan fungsi dalam untuk mengubah nama dari fungsi luarnya. Saya pikir "ikat nama" lebih akurat daripada "ubah variabel".Python 2 tidak memiliki penutupan - memiliki solusi yang menyerupai penutupan.
Ada banyak contoh dalam jawaban yang sudah diberikan - menyalin variabel ke fungsi bagian dalam, memodifikasi objek pada fungsi bagian dalam, dll.
Dalam Python 3, dukungan lebih eksplisit - dan ringkas:
Pemakaian:
Kata
nonlocal
kunci mengikat fungsi dalam ke variabel luar yang disebutkan secara eksplisit, akibatnya melampirkannya. Oleh karena itu lebih eksplisit 'penutupan'.sumber
Saya memiliki situasi di mana saya membutuhkan ruang nama yang terpisah namun persisten. Saya menggunakan kelas. Saya tidak sebaliknya. Nama-nama yang dipisahkan tetapi tetap adalah penutupan.
sumber
Memberi:
Ini adalah contoh dari apa penutupan itu dan bagaimana itu bisa digunakan.
sumber
Saya ingin menawarkan perbandingan sederhana antara contoh python dan JS, jika ini membantu memperjelas.
JS:
dan mengeksekusi:
Python:
dan mengeksekusi:
Alasan: Seperti banyak orang lain katakan di atas, dengan python, jika ada tugas dalam lingkup dalam untuk variabel dengan nama yang sama, referensi baru dalam lingkup dalam dibuat. Tidak demikian halnya dengan JS, kecuali jika Anda secara eksplisit mendeklarasikannya dengan
var
kata kunci.sumber