Untuk kemungkinan blok coba-akhirnya dengan Python, apakah dijamin finally
blok tersebut akan selalu dieksekusi?
Misalnya, saya kembali saat berada di dalam except
blok:
try:
1/0
except ZeroDivisionError:
return
finally:
print("Does this code run?")
Atau mungkin saya meminta kembali Exception
:
try:
1/0
except ZeroDivisionError:
raise
finally:
print("What about this code?")
Pengujian menunjukkan yang finally
dieksekusi untuk contoh di atas, tetapi saya membayangkan ada skenario lain yang belum saya pikirkan.
Apakah ada skenario di mana finally
blok bisa gagal dieksekusi dengan Python?
python
exception-handling
try-catch-finally
finally
Stevoisiak
sumber
sumber
finally
gagal untuk mengeksekusi atau "mengalahkan tujuannya" adalah selama loop tak terbatas,sys.exit
atau interupsi paksa. Status dokumentasi yangfinally
selalu dieksekusi, jadi saya akan melakukannya.finally
tidak akan berjalan. Atau sama jika komputer macet sebelumnya: Dfinally
tidak akan berfungsi jika kabel daya dicabut dari dinding.Jawaban:
"Dijamin" adalah kata yang jauh lebih kuat daripada implementasi apa pun yang
finally
layak. Apa yang dijamin adalah bahwa jika eksekusi mengalir keluar dari keseluruhantry
-finally
konstruksi, itu akan melewatifinally
untuk melakukannya. Apa yang tidak dijamin adalah bahwa eksekusi akan keluar daritry
-finally
.A
finally
di generator atau coroutine async mungkin tidak akan pernah berjalan , jika objek tidak pernah dieksekusi hingga kesimpulan. Ada banyak cara yang bisa terjadi; ini dia:Perhatikan bahwa contoh ini agak rumit: ketika generator dikumpulkan sampah, Python mencoba menjalankan
finally
blok dengan memasukkanGeneratorExit
pengecualian, tetapi di sini kita menangkap pengecualian itu dan sekaliyield
lagi, di mana Python mencetak peringatan ("generator mengabaikan GeneratorExit ") dan menyerah. Lihat PEP 342 (Coroutines via Enhanced Generators) untuk detailnya.Cara lain generator atau coroutine mungkin tidak mengeksekusi kesimpulan termasuk jika objek tidak pernah GC'ed (ya, itu mungkin, bahkan dalam CPython), atau jika
async with
await
dalam__aexit__
, atau jika objekawait
s atauyield
s dalamfinally
blok. Daftar ini tidak dimaksudkan untuk menjadi lengkap.Sebuah
finally
utas daemon mungkin tidak akan pernah dijalankan jika semua utas non-daemon keluar lebih dulu.os._exit
akan segera menghentikan proses tanpa mengeksekusifinally
blok.os.fork
dapat menyebabkanfinally
blok dieksekusi dua kali . Selain masalah normal yang Anda harapkan dari hal-hal yang terjadi dua kali, hal ini dapat menyebabkan konflik akses bersamaan (mogok, terhenti, ...) jika akses ke sumber daya bersama tidak disinkronkan dengan benar .Karena
multiprocessing
menggunakan fork-without-exec untuk membuat proses pekerja saat menggunakan metode start fork (default pada Unix), lalu memanggilos._exit
pekerja setelah pekerjaan pekerja selesai,finally
danmultiprocessing
interaksi bisa menjadi masalah ( contoh ).finally
blok berjalan.kill -SIGKILL
akan mencegahfinally
blokir berjalan.SIGTERM
danSIGHUP
juga akan mencegahfinally
blok berjalan kecuali Anda memasang penangan untuk mengontrol sendiri pematian itu; secara default, Python tidak menanganiSIGTERM
atauSIGHUP
.finally
dapat mencegah pembersihan agar tidak selesai. Satu kasus yang sangat penting adalah jika pengguna menekan control-C tepat saat kita mulai mengeksekusifinally
pemblokiran. Python akan menaikkan aKeyboardInterrupt
dan melewati setiap baris kontenfinally
blok. (KeyboardInterrupt
-kode aman sangat sulit untuk ditulis).finally
pemblokiran tidak akan berjalan.The
finally
blok bukanlah sistem transaksi; itu tidak memberikan jaminan atomicity atau semacamnya. Beberapa dari contoh ini mungkin tampak jelas, tetapi mudah untuk melupakan hal-hal seperti itu dapat terjadi danfinally
terlalu diandalkan .sumber
except
, dan jangan pernah masuk keGeneratorExit
dalam generator. Poin-poin tentang utas / mematikan proses / segfaulting / power off diharapkan, python tidak bisa melakukan sihir. Juga: pengecualian dalamfinally
jelas masalah tetapi ini tidak mengubah fakta bahwa aliran kontrol itu dipindahkan kefinally
blok. TerkaitCtrl+C
, Anda dapat menambahkan penangan sinyal yang mengabaikannya, atau hanya "menjadwalkan" pematian bersih setelah operasi saat ini selesai.kill -9
tidak menentukan bahasa. Dan sejujurnya, itu perlu diulang, karena berada di titik buta. Terlalu banyak orang yang lupa, atau tidak menyadari, bahwa program mereka dapat terhenti di jalurnya bahkan tanpa diizinkan untuk dibersihkan.finally
blok seolah-olah mereka memberikan jaminan transaksional. Mungkin terlihat jelas bahwa mereka tidak melakukannya, tetapi itu bukanlah sesuatu yang disadari semua orang. Adapun kasus genset, ada banyak cara generator mungkin tidak GC'ed sama sekali, dan banyak cara generator atau coroutine mungkin sengaja menghasilkan setelahGeneratorExit
bahkan jika itu tidak menangkapGeneratorExit
, misalnya jikaasync with
menunda coroutine di__exit__
.Iya. Akhirnya selalu menang.
Satu-satunya cara untuk mengalahkannya adalah dengan menghentikan eksekusi sebelum
finally:
mendapat kesempatan untuk mengeksekusinya (misalnya, crash interpreter, matikan komputer Anda, tangguhkan generator selamanya).Berikut ini beberapa lagi yang mungkin belum Anda pikirkan:
Bergantung pada cara Anda keluar dari penerjemah, terkadang Anda akhirnya dapat "membatalkan", tetapi tidak seperti ini:
Menggunakan precarious
os._exit
(ini termasuk dalam "crash the interpreter" menurut saya):Saat ini saya menjalankan kode ini, untuk menguji apakah akhirnya masih akan mengeksekusi setelah kematian panas alam semesta:
Namun, saya masih menunggu hasilnya, jadi periksa lagi nanti.
sumber
finally
di generator atau coroutine dapat dengan mudah gagal dijalankan , tanpa mendekati kondisi "crash the interpreter".sleep(1)
pasti akan menghasilkan perilaku yang tidak terdefinisi. :-Dos._exit
adalah, untuk semua tujuan praktis, sama dengan menyebabkan crash (keluar yang tidak bersih). Cara keluar yang benar adalahsys.exit
.Menurut dokumentasi Python :
Perlu juga dicatat bahwa jika ada beberapa pernyataan pengembalian, termasuk satu di blok akhirnya, maka pengembalian blok terakhir adalah satu-satunya yang akan dieksekusi.
sumber
Ya dan tidak.
Yang dijamin adalah Python akan selalu mencoba mengeksekusi blok terakhir. Dalam kasus di mana Anda kembali dari blok atau memunculkan pengecualian yang tidak tertangkap, blok terakhir dijalankan tepat sebelum benar-benar mengembalikan atau meningkatkan pengecualian.
(apa yang dapat Anda kendalikan sendiri hanya dengan menjalankan kode di pertanyaan Anda)
Satu-satunya kasus yang dapat saya bayangkan di mana blok terakhir tidak akan dieksekusi adalah ketika juru bahasa Python itu sendiri crash misalnya di dalam kode C atau karena pemadaman listrik.
sumber
Saya menemukan yang ini tanpa menggunakan fungsi generator:
Tidur dapat berupa kode apa pun yang mungkin berjalan untuk waktu yang tidak konsisten.
Apa yang tampaknya terjadi di sini adalah bahwa proses paralel pertama yang diselesaikan meninggalkan blok percobaan dengan sukses, tetapi kemudian mencoba mengembalikan dari fungsi sebuah nilai (foo) yang belum didefinisikan di mana pun, yang menyebabkan pengecualian. Pengecualian itu membunuh peta tanpa membiarkan proses lain mencapai blok akhirnya.
Juga, jika Anda menambahkan baris
bar = bazz
tepat setelah panggilan sleep () di blok coba. Kemudian proses pertama untuk mencapai garis itu mengeluarkan pengecualian (karena bazz tidak ditentukan), yang menyebabkan blok akhirnya sendiri dijalankan, tetapi kemudian membunuh peta, menyebabkan blok percobaan lainnya menghilang tanpa mencapai blok akhirnya, dan proses pertama untuk tidak mencapai pernyataan pengembaliannya.Apa artinya ini untuk multiprocessing Python adalah Anda tidak dapat mempercayai mekanisme penanganan pengecualian untuk membersihkan sumber daya di semua proses jika salah satu proses dapat memiliki pengecualian. Penanganan sinyal tambahan atau pengelolaan sumber daya di luar panggilan peta multiprosesing akan diperlukan.
sumber
Tambahkan jawaban yang diterima, hanya untuk membantu melihat cara kerjanya, dengan beberapa contoh:
Ini:
akan mengeluarkan
akan mengeluarkan
sumber