class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
di atas gagal dengan pengecualian AttributeError. Saya mengerti Python tidak menjamin keberadaan "variabel global" (data anggota dalam konteks ini?) Ketika __del__()
dipanggil. Jika itu yang terjadi dan ini adalah alasan pengecualian, bagaimana cara memastikan objek rusak dengan benar?
python
destructor
wilhelmtell
sumber
sumber
__del__
tidak boleh digunakan sebagai mitra__init__
. (Yaitu, itu bukan "penghancur" dalam arti yang__init__
merupakan konstruktor.Jawaban:
Saya akan merekomendasikan menggunakan
with
pernyataan Python untuk mengelola sumber daya yang perlu dibersihkan. Masalah dengan menggunakanclose()
pernyataan eksplisit adalah Anda harus khawatir tentang orang yang lupa menyebutnya sama sekali atau lupa menempatkannya difinally
blok untuk mencegah kebocoran sumber daya ketika pengecualian terjadi.Untuk menggunakan
with
pernyataan, buat kelas dengan metode berikut:Dalam contoh Anda di atas, Anda akan menggunakan
Kemudian, ketika seseorang ingin menggunakan kelas Anda, mereka akan melakukan hal berikut:
Variabel package_obj akan menjadi turunan dari tipe Paket (ini adalah nilai yang dikembalikan oleh
__enter__
metode). Its__exit__
metode secara otomatis akan dipanggil, terlepas dari apakah atau tidak pengecualian terjadi.Anda bahkan bisa mengambil pendekatan ini selangkah lebih maju. Pada contoh di atas, seseorang masih dapat membuat Instantiate menggunakan konstruktornya tanpa menggunakan
with
klausa. Anda tidak ingin itu terjadi. Anda bisa memperbaikinya dengan membuat kelas PackageResource yang mendefinisikan__enter__
dan__exit__
metode. Kemudian, kelas Paket akan didefinisikan secara ketat di dalam__enter__
metode dan dikembalikan. Dengan begitu, penelepon tidak pernah dapat membuat instance kelas Paket tanpa menggunakanwith
pernyataan:Anda akan menggunakan ini sebagai berikut:
sumber
with Resource(param1, param2) as r: # ...
Cara standar adalah dengan menggunakan
atexit.register
:Tetapi Anda harus ingat bahwa ini akan bertahan semua contoh yang dibuat
Package
sampai Python dihentikan.Demo menggunakan kode di atas disimpan sebagai package.py :
sumber
with
? Apakah mereka secara eksplisit memanggil__enter__
?) Kelemahannya tentu saja jika Anda memerlukan pembersihan untuk terjadi sebelum python keluar, itu tidak akan berhasil. Dalam kasus saya, saya tidak peduli apakah itu ketika objek keluar dari ruang lingkup atau jika tidak sampai python keluar. :)atexit.register(self.__exit__)
?__exit__
, dan menggunakan manajer konteks? Juga,__exit__
ambil argumen tambahan (mis.__exit__(self, type, value, traceback)
), Jadi Anda perlu mengakses untuk itu. Either way, sepertinya Anda harus memposting pertanyaan terpisah pada SO, karena kasus penggunaan Anda tampak tidak biasa?Sebagai lampiran dari jawaban Clint , Anda dapat menyederhanakan
PackageResource
menggunakancontextlib.contextmanager
:Atau, meskipun mungkin bukan sebagai Pythonic, Anda dapat mengesampingkan
Package.__new__
:dan cukup gunakan
with Package(...) as package
.Untuk mendapatkan yang lebih singkat, beri nama fungsi pembersihan Anda
close
dan gunakancontextlib.closing
, dalam hal ini Anda dapat menggunakanPackage
kelas yang tidak dimodifikasi melaluiwith contextlib.closing(Package(...))
atau menimpanya__new__
ke yang lebih sederhanaDan konstruktor ini diwarisi, jadi Anda bisa mewarisi, misalnya
sumber
Package.__new__()
metode, bagaimanapun. Atau mungkin kita bisa. Kita mungkin bisa mendefinisikan dekorator kelas atau metaclass genericizing bahwa boilerplate untuk kita. Makanan untuk pemikiran Pythonic.Package
juga harus melakukan ini (meskipun saya belum mengujinya), jadi tidak diperlukan metaclass. Meskipun saya telah menemukan beberapa cara yang cukup aneh untuk menggunakan metaclasses di masa lalu ...Package
(atau lebih baik nama kelasClosing
) sebagai induk kelas Anda, bukanobject
. Tapi jangan tanya saya berapa banyak warisan yang kacau dengan ini ...Saya tidak berpikir bahwa mungkin anggota misalnya untuk dihapus sebelum
__del__
dipanggil. Dugaan saya adalah bahwa alasan untuk AttributeError khusus Anda adalah di tempat lain (mungkin Anda salah menghapus self.file di tempat lain).Namun, seperti yang ditunjukkan orang lain, Anda harus menghindari penggunaan
__del__
. Alasan utama untuk ini adalah bahwa instance dengan__del__
tidak akan menjadi sampah yang dikumpulkan (mereka hanya akan dibebaskan ketika refcount mereka mencapai 0). Oleh karena itu, jika instance Anda terlibat dalam referensi melingkar, mereka akan hidup dalam memori selama aplikasi berjalan. (Saya mungkin salah tentang semua ini, saya harus membaca gc docs lagi, tapi saya agak yakin ini berfungsi seperti ini).sumber
__del__
dapat berupa sampah yang dikumpulkan jika referensi mereka dihitung dari objek lain dengan__del__
nol dan tidak dapat dijangkau. Ini berarti jika Anda memiliki siklus referensi antara objek dengan__del__
, tidak ada yang akan dikumpulkan. Namun, kasus lain harus diselesaikan seperti yang diharapkan.Alternatif yang lebih baik adalah dengan menggunakan lemahref . Finalisasi . Lihat contoh di Finalizer Objects dan Membandingkan finalizer dengan metode __del __ () .
sumber
stop()
metode untuk menutup port danjoin()
proses. Namun, jika program keluar secarastop()
tidak terduga tidak dipanggil - saya menyelesaikannya dengan finalizer. Tetapi bagaimanapun saya memanggil_finalizer.detach()
metode stop untuk mencegah memanggilnya dua kali (secara manual dan kemudian lagi oleh finalizer).Saya pikir masalahnya bisa di
__init__
jika ada lebih banyak kode daripada yang ditunjukkan?__del__
akan dipanggil bahkan ketika__init__
belum dieksekusi dengan benar atau melemparkan pengecualian.Sumber
sumber
__del__
adalah dengan secara eksplisit mendeklarasikan semua anggota di tingkat kelas, memastikan bahwa mereka selalu ada, bahkan jika__init__
gagal. Dalam contoh yang diberikan,files = ()
akan berhasil, meskipun sebagian besar Anda hanya akan menetapkanNone
; dalam kedua kasus, Anda masih perlu menetapkan nilai sebenarnya di__init__
.Berikut ini kerangka kerja minimal:
Penting: kembalikan diri
Jika Anda seperti saya, dan mengabaikan
return self
bagian (dari jawaban Clint Miller yang benar ), Anda akan menatap omong kosong ini:Semoga ini bisa membantu orang selanjutnya.
sumber
Hanya bungkus destruktor Anda dengan pernyataan try / kecuali dan itu tidak akan membuang pengecualian jika global Anda sudah dibuang.
Edit
Coba ini:
Ini akan memasukkan daftar file dalam fungsi del yang dijamin ada pada saat panggilan. Proxy lemahref adalah untuk mencegah Python, atau Anda sendiri menghapus variabel self.files, entah bagaimana (jika dihapus, maka itu tidak akan memengaruhi daftar file asli). Jika bukan karena ini sedang dihapus meskipun ada lebih banyak referensi ke variabel, maka Anda dapat menghapus enkapsulasi proxy.
sumber
Tampaknya cara idiomatis untuk melakukan ini adalah dengan memberikan
close()
metode (atau yang serupa), dan menyebutnya dengan jelas.sumber