Mengapa kita membutuhkan klausa "akhirnya" dengan Python?

306

Saya tidak yakin mengapa kita perlu finallydalam try...except...finallypernyataan. Menurut pendapat saya, blok kode ini

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

sama dengan yang ini menggunakan finally:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

Apakah saya melewatkan sesuatu?

RNA
sumber

Jawaban:

422

Itu membuat perbedaan jika Anda kembali lebih awal:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   # The finally block is run before the method returns
finally:
    other_code()

Bandingkan dengan ini:

try:
    run_code1()
except TypeError:
    run_code2()
    return None   

other_code()  # This doesn't get run if there's an exception.

Situasi lain yang dapat menyebabkan perbedaan:

  • Jika pengecualian dilemparkan ke dalam blok kecuali.
  • Jika ada pengecualian run_code1()tapi itu bukan TypeError.
  • Pernyataan aliran kontrol lainnya seperti continuedan breakpernyataan.
Mark Byers
sumber
1
coba: #x = Halo + 20 x = 10 + 20 kecuali: cetak 'Saya masuk kecuali blok' x = 20 + 30 lainnya: cetak 'Saya di blok lain' x + = 1 akhirnya: cetak 'Akhirnya x =% s '% (x)
Abhijit Sahu
89

Anda dapat menggunakan finallyuntuk memastikan file atau sumber daya ditutup atau dirilis terlepas dari apakah pengecualian terjadi, bahkan jika Anda tidak menangkap pengecualian. (Atau jika Anda tidak menangkap pengecualian spesifik itu .)

myfile = open("test.txt", "w")

try:
    myfile.write("the Answer is: ")
    myfile.write(42)   # raises TypeError, which will be propagated to caller
finally:
    myfile.close()     # will be executed before TypeError is propagated

Dalam contoh ini Anda akan lebih baik menggunakan withpernyataan, tetapi struktur semacam ini dapat digunakan untuk jenis sumber daya lainnya.

Beberapa tahun kemudian, saya menulis posting blog tentang penyalahgunaan finallyyang menurut pembaca lucu.

baik hati
sumber
23

Mereka tidak setara. Akhirnya kode dijalankan apa pun yang terjadi. Ini berguna untuk kode pembersihan yang harus dijalankan.

Antimon
sumber
15
Finally code is run no matter what else happens... kecuali ada loop tak terbatas. Atau steker listrik. Atau os._exit(). Atau ...
Mark Byers
3
@Ark Sebenarnya, sys.exit melempar pengecualian normal. Tapi ya, apa pun yang menyebabkan proses untuk segera dihentikan akan berarti bahwa tidak ada yang dijalankan.
Antimony
1
@Antimony: Terima kasih. Diubah menjadi os._exit.
Mark Byers
Hanya ingin tahu, mengapa kode pembersihan tidak dapat ditempatkan di dalam kecuali jika kode akan dimasukkan kecuali hanya jika ditemukan pengecualian?
Stephen Jacob
2
@Stephen Untuk satu hal, akhirnya kode berjalan walaupun Anda kembali dari blok try. Dalam hal ini, Anda tidak akan menekan klausa kecuali.
Antimony
18

Untuk menambah jawaban lain di atas, finallyklausa mengeksekusi tidak peduli apa sedangkan elseklausa mengeksekusi hanya jika pengecualian tidak dimunculkan.

Misalnya, menulis ke file tanpa pengecualian akan menghasilkan yang berikut:

file = open('test.txt', 'w')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

KELUARAN:

Writing to file.
Write successful.
File closed.

Jika ada pengecualian, kode akan menampilkan yang berikut, (perhatikan bahwa kesalahan yang disengaja disebabkan oleh menjaga file hanya-baca.

file = open('test.txt', 'r')

try:
    file.write("Testing.")
    print("Writing to file.")
except IOError:
    print("Could not write to file.")
else:
    print("Write successful.")
finally:
    file.close()
    print("File closed.")

KELUARAN:

Could not write to file.
File closed.

Kita bisa melihat bahwa finallyklausa dieksekusi terlepas dari pengecualian. Semoga ini membantu.

kapten hitam
sumber
2
Ini akan berhasil bahkan jika Anda tidak menggunakan klausul "akhirnya" yang tidak menjawab pertanyaan karena OP ingin mengetahui perbedaannya, contoh yang baik akan menyebabkan kesalahan yang berbeda dari IOError, untuk menunjukkan bahwa akhirnya blok klausa dieksekusi sebelum pengecualian disebarkan ke pemanggil.
Reda Drissi
2
Saya tidak tahu elseapa-apa. Berguna untuk tahu.
mazunki
8

Blok kode tidak setara. The finallyklausul juga akan dijalankan jika run_code1()melempar pengecualian selain TypeError, atau jika run_code2()melempar pengecualian, sedangkan other_code()pada versi pertama tidak akan dijalankan dalam kasus ini.

Sven Marnach
sumber
7

Dalam contoh pertama Anda, apa yang terjadi jika run_code1()memunculkan pengecualian yang tidak TypeError? ... other_code()tidak akan dieksekusi.

Bandingkan dengan finally:versi: other_code()dijamin akan dieksekusi terlepas dari segala pengecualian yang diajukan.

mhawke
sumber
7

Sebagaimana dijelaskan dalam dokumentasi , finallyklausa ini dimaksudkan untuk mendefinisikan tindakan pembersihan yang harus dijalankan dalam semua keadaan .

Jika finallyada, itu menentukan penangan 'pembersihan'. Itutry klausul dijalankan, termasuk exceptdan elseklausa. Jika pengecualian terjadi di salah satu klausa dan tidak ditangani, pengecualian disimpan sementara. The finallyklausul dijalankan. Jika ada pengecualian yang disimpan maka akan muncul kembali di akhir finally klausa.

Sebuah contoh:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Seperti yang Anda lihat, finally klausa dieksekusi dalam peristiwa apa pun. The TypeErrormengangkat dengan membagi dua string tidak ditangani oleh exceptklausa dan karena itu kembali mengangkat setelah finallyklausul telah dieksekusi.

Dalam aplikasi dunia nyata, klausa terakhir berguna untuk melepaskan sumber daya eksternal (seperti file atau koneksi jaringan), terlepas dari apakah penggunaan sumber daya berhasil.

Eugene Yarmash
sumber
4

Contoh sempurna adalah sebagai berikut:

try:
    #x = Hello + 20
    x = 10 + 20 
except:
    print 'I am in except block'
    x = 20 + 30
else:
    print 'I am in else block'
    x += 1
finally:
    print 'Finally x = %s' %(x)
Abhijit Sahu
sumber
3

finallyadalah untuk mendefinisikan "tindakan pembersihan" . The finallyklausul dijalankan dalam setiap peristiwa sebelum meninggalkantry pernyataan, apakah pengecualian (bahkan jika Anda tidak menanganinya) telah terjadi atau tidak.

Saya beri contoh @ Byers.

kakhkAtion
sumber
2

Akhirnya dapat juga digunakan ketika Anda ingin menjalankan kode "opsional" sebelum menjalankan kode untuk pekerjaan utama Anda dan bahwa kode opsional mungkin gagal karena berbagai alasan.

Dalam contoh berikut, kami tidak tahu persisnya pengecualian apa yang store_some_debug_infomungkin muncul.

Kita bisa lari:

try:
  store_some_debug_info()
except Exception:
  pass
do_something_really_important() 

Tapi, sebagian besar linter akan mengeluh tentang menangkap pengecualian yang terlalu kabur. Juga, karena kami memilih hanya passuntuk kesalahan, exceptblok tidak benar-benar menambah nilai.

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

Kode di atas memiliki efek yang sama dengan blok kode 1 tetapi lebih ringkas.

Brad Johnson
sumber
2

Menggunakan delphi secara profesional selama beberapa tahun mengajarkan saya untuk menjaga rutinitas pembersihan saya menggunakan akhirnya. Delphi cukup banyak memberlakukan penggunaan akhirnya untuk membersihkan semua sumber daya yang dibuat sebelum blok coba, jangan sampai Anda menyebabkan kebocoran memori. Ini juga cara kerja Java, Python dan Ruby.

resource = create_resource
try:
  use resource
finally:
  resource.cleanup

dan sumber daya akan dibersihkan terlepas dari apa yang Anda lakukan antara mencoba dan akhirnya. Juga, itu tidak akan dibersihkan jika eksekusi tidak pernah mencapai tryblok. (Yaitu create_resourcesendiri melempar pengecualian) Itu membuat kode Anda "pengecualian aman".

Mengapa Anda benar-benar membutuhkan blokir terakhir, tidak semua bahasa membutuhkannya. Di C ++ di mana Anda secara otomatis memanggil destructors yang memberlakukan pembersihan ketika pengecualian membuka gulungan stack. Saya pikir ini adalah langkah maju menuju kode bersih dibandingkan dengan mencoba ... akhirnya bahasa.

{    
  type object1;
  smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
nurettin
sumber
2

Blok percobaan hanya memiliki satu klausa wajib: Pernyataan percobaan. Klausa kecuali, yang lain, dan terakhir adalah opsional dan didasarkan pada preferensi pengguna.

akhirnya: Sebelum Python meninggalkan pernyataan coba, itu akan menjalankan kode di blok akhirnya dalam kondisi apa pun, bahkan jika itu mengakhiri program. Misalnya, jika Python mengalami kesalahan saat menjalankan kode di blok kecuali atau yang lain, blok akhirnya masih akan dieksekusi sebelum menghentikan program.

Lawrence Krukrubo
sumber
1
Ini salah. Pernyataan kecuali wajib. - Lucas Azevedo 1 Februari jam 12:04 Ini salah, karena saya baru saja mengkompilasi dan menjalankan program Python 3.5 dengan blok coba-akhirnya, tanpa klausa "kecuali".
Rob Tow
2
Saya mencoba ini sendiri dan tidak percaya, klausa kecuali tidak wajib.
captainblack
1

Jalankan kode Python3 ini untuk melihat kebutuhan akhirnya:

KASUS 1:

count = 0
while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")
        finally:
            print("Your Attempts: {}".format(count))

CASE2:

count = 0

while True:
    count += 1
    if count > 3:
        break
    else:
        try:
            x = int(input("Enter your lock number here: "))

            if x == 586:
                print("Your lock has unlocked :)")

                break
            else:
                print("Try again!!")

                continue

        except:
            print("Invalid entry!!")

        print("Your Attempts: {}".format(count))

Coba input berikut ini setiap kali:

  1. bilangan bulat acak
  2. kode yang benar yaitu 586 (Coba ini dan Anda akan mendapatkan jawaban Anda)
  3. string acak

** Pada tahap awal pembelajaran Python.

AshPython
sumber
1

Saya mencoba menjalankan kode di mana saya ingin membaca lembar excel. Masalahnya adalah, jika ada file yang tidak memiliki sheet bernama say: SheetSum Saya tidak dapat memindahkannya ke lokasi kesalahan !! Kode yang saya tulis adalah:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Memberi kesalahan:

[WinError 32] Proses tidak dapat mengakses file karena sedang digunakan oleh proses lain

Saya harus menambahkan try except with finallyblok penuh dan memberi tahu finallysaya harus menutup file dalam hal apa pun seperti:

def read_file(data_file):
    # data_file = '\rr\ex.xlsx'
    sheets = {}
    try:
        print("Reading file: "+data_file)
        sheets_file = open(data_file,'rb')
        sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
    except Exception as excpt:
        print("Exception occurred", exc_info=True)
    finally:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

Jika tidak, file tetap terbuka adalah latar belakang.

Jika finallyada, itu menentukan penangan pembersihan . The try klausul dijalankan, termasuk exceptdan elseklausa. Jika pengecualian terjadi di salah satu klausa dan tidak ditangani, pengecualian disimpan sementara . The finallyklausul dijalankan. Jika ada pengecualian yang disimpan maka akan muncul kembali di akhir finally klausa. Jika finallyklausa menimbulkan pengecualian lain, pengecualian yang disimpan diatur sebagai konteks pengecualian baru.

..Lebih Di Sini

Saqib Mujtaba
sumber