Apa itu metode __del__, Bagaimana menyebutnya?

108

Saya sedang membaca kode. Ada kelas di mana __del__metode didefinisikan. Saya menemukan bahwa metode ini digunakan untuk menghancurkan sebuah instance dari kelas. Namun, saya tidak dapat menemukan tempat di mana metode ini digunakan. Alasan utama untuk itu adalah bahwa saya tidak tahu bagaimana metode ini digunakan, mungkin tidak seperti itu: obj1.del(). Jadi, pertanyaan saya adalah bagaimana memanggil __del__metode tersebut?

Verrtex
sumber

Jawaban:

168

__del__adalah finalisator . Ini dipanggil ketika sebuah objek dikumpulkan sampah yang terjadi di beberapa titik setelah semua referensi ke objek telah dihapus.

Dalam kasus sederhana ini bisa terjadi tepat setelah Anda mengatakan del xatau, jika xadalah variabel lokal, setelah fungsi berakhir. Secara khusus, kecuali ada referensi melingkar, CPython (implementasi Python standar) akan segera mengumpulkan sampah.

Namun, ini adalah detail implementasi CPython. Satu-satunya properti yang diperlukan dari pengumpulan sampah Python adalah bahwa ini terjadi setelah semua referensi dihapus, jadi ini mungkin tidak perlu terjadi segera setelah itu dan mungkin tidak terjadi sama sekali .

Terlebih lagi, variabel dapat hidup untuk waktu yang lama karena berbagai alasan , misalnya pengecualian yang menyebar atau introspeksi modul dapat membuat jumlah referensi variabel lebih besar dari 0. Selain itu, variabel dapat menjadi bagian dari siklus referensi - CPython dengan pengumpulan sampah dihidupkan paling banyak , tapi tidak semua, siklus seperti itu, itupun hanya secara berkala.

Karena Anda tidak memiliki jaminan bahwa itu dijalankan, seseorang tidak boleh memasukkan kode yang Anda perlukan __del__()- sebaliknya, kode ini milik finallyklausul tryblok atau manajer konteks dalam sebuah withpernyataan. Namun, ada kasus penggunaan yang valid untuk __del__: mis. Jika sebuah objek Xmereferensikan Ydan juga menyimpan salinan Yreferensi dalam global cache( cache['X -> Y'] = Y) maka akan lebih sopan jika X.__del__juga menghapus entri cache.

Jika Anda tahu bahwa destructor menyediakan (melanggar pedoman di atas) pembersihan diperlukan, Anda mungkin ingin menyebutnya langsung , karena tidak ada yang khusus tentang hal itu sebagai metode: x.__del__(). Jelas, Anda harus melakukannya hanya jika Anda tahu bahwa tidak keberatan untuk dipanggil dua kali. Atau, sebagai upaya terakhir, Anda dapat mendefinisikan kembali metode ini menggunakan

type(x).__del__ = my_safe_cleanup_method  
ilya n.
sumber
5
Anda mengatakan bahwa fitur CPython untuk menghapus objek segera setelah jumlah referensi dikurangi menjadi nol adalah "detail implementasi". Saya tidak yakin Dapatkah Anda memberikan link untuk mendukung klaim tersebut? (Maksudku, huruf tebal adalah cukup meyakinkan sendiri, tapi link adalah dekat kedua ... :-)
Stuart Berg
14
Detail implementasi CPython: CPython saat ini menggunakan skema penghitungan referensi dengan deteksi tertunda (opsional) dari sampah yang terhubung secara siklis, ... Implementasi lain bertindak berbeda dan CPython dapat berubah. ( docs.python.org/2/reference/datamodel.html )
ilya n.
Ada apa __exit__dalam konteks ini? Apakah itu berjalan setelah atau sebelum __del__atau bersama?
lony
1
Apakah "mungkin tidak terjadi sama sekali" termasuk saat program dihentikan?
Andy Hayden
1
@AndyHayden: __del__metode mungkin tidak berjalan bahkan pada penghentian program, dan bahkan ketika mereka berjalan saat penghentian, menulis __del__metode yang bekerja dengan baik bahkan saat penerjemah sibuk merusak diri sendiri di sekitar Anda membutuhkan pengkodean yang lebih hati-hati daripada yang diterapkan banyak pemrogram. (Pembersihan CPython biasanya mendapatkan __del__metode untuk dijalankan saat penafsiran shutdown, tetapi masih ada kasus di mana itu tidak cukup. Daemon threads, C-level global, dan objek yang __del__dibuat dengan yang lain __del__semua dapat menyebabkan __del__metode tidak berjalan.)
user2357112 mendukung Monica
80

Saya menulis jawaban untuk pertanyaan lain, meskipun ini adalah pertanyaan yang lebih akurat untuk itu.

Bagaimana cara kerja konstruktor dan destruktor?

Ini adalah jawaban yang sedikit beropini.

Jangan gunakan __del__. Ini bukan C ++ atau bahasa yang dibuat untuk destruktor. The __del__Metode benar-benar harus pergi dengan Python 3.x, meskipun aku yakin seseorang akan menemukan kasus penggunaan yang masuk akal. Jika Anda perlu menggunakan __del__, perhatikan batasan dasar per http://docs.python.org/reference/datamodel.html :

  • __del__dipanggil saat pengumpul sampah kebetulan sedang mengumpulkan objek, bukan saat Anda kehilangan referensi terakhir ke suatu objek dan bukan saat Anda mengeksekusinya del object.
  • __del__bertanggung jawab untuk memanggil salah __del__satu superclass, meskipun tidak jelas apakah ini dalam urutan resolusi metode (MRO) atau hanya memanggil setiap superclass.
  • Memiliki __del__sarana agar pengumpul sampah menyerah untuk mendeteksi dan membersihkan tautan siklik, seperti kehilangan referensi terakhir ke daftar tertaut. Anda bisa mendapatkan daftar objek yang diabaikan dari gc.garbage. Terkadang Anda dapat menggunakan referensi yang lemah untuk menghindari siklus sama sekali. Hal ini kadang-kadang diperdebatkan: lihat http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • The __del__Fungsi bisa menipu, menyimpan referensi ke sebuah obyek, dan menghentikan pengumpulan sampah.
  • Pengecualian yang secara eksplisit dimunculkan __del__akan diabaikan.
  • __del__melengkapi __new__lebih dari __init__. Ini membingungkan. Lihat http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the-berlawanan-of- init / untuk penjelasan dan penjelasan.
  • __del__bukanlah anak yang "dicintai" dengan Python. Anda akan melihat bahwa dokumentasi sys.exit () tidak menentukan apakah sampah dikumpulkan sebelum keluar, dan ada banyak masalah aneh. Memanggil __del__on global menyebabkan masalah pengurutan yang aneh, misalnya, http://bugs.python.org/issue5099 . Haruskah __del__dipanggil bahkan jika __init__gagal? Lihat http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 untuk utas panjang.

Tetapi di sisi lain:

Dan alasan pesonal saya untuk tidak menyukai __del__fungsinya.

  • Setiap kali seseorang mengungkitnya __del__, pesan itu berubah menjadi tiga puluh pesan kebingungan.
  • Itu merusak item-item ini di Zen of Python:
    • Sederhana lebih baik daripada rumit.
    • Kasus khusus tidak cukup istimewa untuk melanggar aturan.
    • Kesalahan tidak boleh lewat diam-diam.
    • Dalam menghadapi ambiguitas, tolak godaan untuk menebak.
    • Harus ada satu - dan sebaiknya hanya satu - cara yang jelas untuk melakukannya.
    • Jika implementasinya sulit dijelaskan, itu ide yang buruk.

Jadi, temukan alasan untuk tidak menggunakan __del__.

Charles Merriam
sumber
6
Sekalipun pertanyaannya tidak persis: mengapa kita tidak menggunakan __del__, tetapi bagaimana memanggil __del__, jawaban Anda menarik.
nbro
Terima kasih. Terkadang ide terbaik adalah menjauhi ide-ide buruk.
Charles Merriam
Dalam berita lain, saya lupa menyebutkan bahwa PyPy (penerjemah yang lebih cepat untuk aplikasi yang berjalan lebih lama) akan rusak di del .
Charles Merriam
Terima kasih @Gloin karena telah memperbarui tautan yang rusak!
Charles Merriam
@CharlesMerriam Terima Anda untuk jawabannya!
Tom Burrows
13

The __del__metode, itu akan dipanggil ketika objek adalah sampah yang dikumpulkan. Perhatikan bahwa itu belum tentu dijamin akan dipanggil. Kode berikut dengan sendirinya belum tentu bisa melakukannya:

del obj

Alasannya karena delhanya mengurangi jumlah referensi satu per satu. Jika sesuatu yang lain memiliki referensi ke objek tersebut, __del__tidak akan dipanggil.

Ada beberapa peringatan untuk digunakan __del__. Umumnya, mereka biasanya tidak terlalu berguna. Bagi saya kedengarannya lebih seperti Anda ingin menggunakan metode dekat atau mungkin pernyataan with .

Lihat dokumentasi python tentang __del__metode .

Satu hal lagi yang perlu diperhatikan: __del__metode dapat menghambat pengumpulan sampah jika digunakan secara berlebihan. Secara khusus, referensi melingkar yang memiliki lebih dari satu objek dengan __del__metode tidak akan mengumpulkan sampah. Ini karena pengumpul sampah tidak tahu mana yang harus dipanggil terlebih dahulu. Lihat dokumentasi pada modul gc untuk info lebih lanjut.

Jason Baker
sumber
8

The __del__Metode (catatan ejaan!) Dipanggil saat objek Anda akhirnya hancur. Secara teknis (dalam cPython) saat itu tidak ada lagi referensi ke objek Anda, yaitu saat objek tersebut berada di luar jangkauan.

Jika Anda ingin menghapus objek Anda dan memanggil __del__metode use

del obj1

yang akan menghapus objek (asalkan tidak ada referensi lain untuk itu).

Saya sarankan Anda menulis kelas kecil seperti ini

class T:
    def __del__(self):
        print "deleted"

Dan selidiki di penerjemah python, misalnya

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Perhatikan bahwa jython dan ironpython memiliki aturan berbeda tentang kapan tepatnya objek dihapus dan __del__dipanggil. Ini tidak dianggap sebagai praktik yang baik untuk digunakan __del__karena ini dan fakta bahwa objek dan lingkungannya mungkin dalam keadaan tidak diketahui saat dipanggil. Itu tidak sepenuhnya dijamin __del__akan dipanggil - penerjemah dapat keluar dengan berbagai cara tanpa menghapus semua objek.

Nick Craig-Wood
sumber
1
dibandingkan dengan stackoverflow.com/a/2452895/611007 dan stackoverflow.com/a/1481512/611007 , use del obj1sepertinya ide yang buruk untuk diandalkan.
n611x007
0

Seperti yang disebutkan sebelumnya, __del__fungsinya agak tidak dapat diandalkan. Dalam kasus di mana mungkin tampak berguna, pertimbangkan untuk menggunakan metode __enter__dan __exit__sebagai gantinya. Ini akan memberikan perilaku yang mirip dengan with open() as f: passsintaks yang digunakan untuk mengakses file. __enter__secara otomatis dipanggil saat memasuki cakupan with, sementara __exit__secara otomatis dipanggil saat keluar. Lihat pertanyaan ini untuk lebih jelasnya.

somoria
sumber