Python coba lagi


581

Apa maksud penggunaan elseklausa opsional dari trypernyataan itu?


1
Sebagian besar jawaban tampaknya berkonsentrasi pada mengapa kita tidak bisa begitu saja meletakkan materi di klausa lain di klausa coba sendiri. Pertanyaan stackoverflow.com/questions/3996329 secara khusus menanyakan mengapa kode klausa lain tidak dapat digunakan setelah blok coba itu sendiri, dan pertanyaan itu ditipu untuk yang satu ini, tapi saya tidak melihat jawaban yang jelas untuk pertanyaan itu di sini. Saya merasa stackoverflow.com/a/3996378/1503120 menjawab dengan baik pertanyaan itu. Saya juga telah mencoba menjelaskan berbagai signifikansi berbagai klausa di stackoverflow.com/a/22579805/1503120 .
jamadagni

Anda ingin sesuatu terjadi jika pengecualian tidak memicu, sebelum pembersihan akhir, yang tidak seharusnya memicu sendiri penanganan pengecualian yang sama.
benjimin

Jawaban:


858

Pernyataan di elseblok dieksekusi jika eksekusi jatuh dari bagian bawah try- jika tidak ada pengecualian. Jujur, saya tidak pernah menemukan kebutuhan.

Namun, Menangani Pengecualian mencatat:

Penggunaan klausa lain lebih baik daripada menambahkan kode tambahan ke klausa coba karena menghindari secara tidak sengaja menangkap pengecualian yang tidak dimunculkan oleh kode yang dilindungi oleh percobaan ... kecuali pernyataan.

Jadi, jika Anda memiliki metode yang dapat, misalnya, melempar IOError, dan Anda ingin menangkap pengecualian yang dimunculkan, tetapi ada hal lain yang ingin Anda lakukan jika operasi pertama berhasil, dan Anda tidak ingin menangkap IOError dari operasi itu, Anda mungkin menulis sesuatu seperti ini:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

Jika Anda hanya memasukkan another_operation_that_can_throw_ioerror()setelah operation_that_can_throw_ioerror, exceptakan menangkap kesalahan panggilan kedua. Dan jika Anda meletakkannya setelah seluruh tryblok, itu akan selalu dijalankan, dan tidak sampai setelah finally. The elsememungkinkan Anda memastikan

  1. operasi kedua hanya berjalan jika tidak terkecuali,
  2. dijalankan sebelum finallyblok, dan
  3. apa pun yang IOErrorditimbulkannya tidak tertangkap di sini

7
Perlu diingat juga bahwa variabel yang digunakan dalam blok coba BISA digunakan dalam blok lain, jadi Anda sebaiknya mempertimbangkan untuk menggunakan varian ini jika Anda tidak mengharapkan lebih banyak pengecualian di blok lain
WorldSEnder

3
Itu tidak masalah, karena variabel try-scoped terlihat di luar coba apakah ada yang lain atau tidak.
Reinderien

36
Tidak ada yang namanya "variabel try-scoped". Dalam Python, lingkup variabel ditetapkan hanya oleh modul, fungsi dan pemahaman, bukan struktur kontrol.
mhsmith

9
Klausa lain memungkinkan Anda menulis kode yang hanya masuk akal jika pengecualian tidak dilontarkan; kecuali klausa hanya bisa lewat. Jika Anda memasukkan logika di blok percobaan, Anda berisiko menyembunyikan bug di kode Anda. Jangan pernah memeras pengecualian yang tidak Anda harapkan.
Alice Purcell

9
tidak jelas dari jawaban ini apa arti "jatuh dari bawah" - tidak hanya ini terjadi karena pengecualian tetapi juga, karena a return, continueatau break.
Antti Haapala

108

Ada satu alasan besar untuk menggunakan else- gaya dan keterbacaan. Biasanya ide yang baik untuk menyimpan kode yang dapat menyebabkan pengecualian di dekat kode yang berhubungan dengannya. Sebagai contoh, bandingkan ini:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

dan

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

Yang kedua bagus ketika excepttidak bisa kembali lebih awal, atau melemparkan kembali pengecualian. Jika memungkinkan, saya akan menulis:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Catatan: Jawaban disalin dari duplikat yang baru-baru ini diposting di sini , maka semua ini adalah "AskPassword".


53

Satu penggunaan: uji beberapa kode yang harus memunculkan pengecualian.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(Kode ini harus diabstraksi menjadi tes yang lebih umum dalam praktiknya.)


50

Python coba lagi

Apa maksud penggunaan elseklausa opsional dari pernyataan try?

Penggunaan yang dimaksudkan adalah memiliki konteks agar lebih banyak kode dapat dijalankan jika tidak ada pengecualian di mana kode itu diharapkan ditangani.

Konteks ini menghindari penanganan kesalahan yang tidak Anda harapkan.

Tapi penting untuk memahami kondisi yang tepat yang menyebabkan klausul lain untuk lari, karena return, continuedan breakdapat mengganggu aliran kontrol untuk else.

Singkatnya

The elsePernyataan berjalan jika ada ada pengecualian dan jika tidak terganggu oleh return, continueatau breakpernyataan.

Jawaban lain melewatkan bagian terakhir itu.

Dari dokumen:

Opsional elseklausul dijalankan jika dan ketika kontrol mengalir dari ujung dari tryklausa. *

(Huruf ditambahkan.) Dan catatan kaki berbunyi:

* Saat ini, kendali “mengalir dari ujung” kecuali dalam kasus pengecualian atau eksekusi dari return, continueatau breakpernyataan.

Itu membutuhkan setidaknya satu sebelumnya kecuali klausa ( lihat tata bahasa ). Jadi itu sebenarnya bukan "coba-lagi," itu "coba-kecuali-lain (-akhirnya)," dengan else(dan finally) menjadi opsional.

The Python Tutorial mengelaborasi penggunaan yang dimaksudkan:

Coba ... kecuali pernyataan memiliki klausa opsional lain, yang, jika ada, harus mengikuti semua kecuali klausa. Ini berguna untuk kode yang harus dijalankan jika klausa coba tidak menimbulkan pengecualian. Sebagai contoh:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

Penggunaan klausa lain lebih baik daripada menambahkan kode tambahan ke klausa coba karena menghindari secara tidak sengaja menangkap pengecualian yang tidak dimunculkan oleh kode yang dilindungi oleh percobaan ... kecuali pernyataan.

Contoh membedakan elseversus kode mengikuti tryblok

Jika Anda menangani kesalahan, elseblok tidak akan berjalan. Sebagai contoh:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

Dan sekarang,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

26

Mencoba-kecuali-yang lain sangat bagus untuk menggabungkan pola EAFP dengan mengetik bebek :

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

Anda mungkin menganggap kode naif ini baik-baik saja:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

Ini adalah cara terbaik untuk menyembunyikan bug parah dalam kode Anda secara tidak sengaja. Saya mengetik pembersihan di sana, tetapi AttributeError yang akan memberi tahu saya sedang ditelan. Lebih buruk lagi, bagaimana jika saya menulisnya dengan benar, tetapi metode pembersihan kadang-kadang diteruskan tipe pengguna yang memiliki atribut salah nama, menyebabkannya gagal setengah jalan melalui dan meninggalkan file tidak tertutup? Semoga berhasil men-debug yang satu itu.


19

Saya merasa sangat berguna ketika Anda memiliki pembersihan untuk melakukan itu harus dilakukan bahkan jika ada pengecualian:

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

9

Meskipun Anda tidak bisa memikirkan penggunaannya sekarang, Anda bisa bertaruh pasti ada gunanya. Berikut adalah contoh yang tidak imajinatif:

Dengan else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Tanpa else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Di sini Anda memiliki variabel yang somethingditentukan jika tidak ada kesalahan yang terjadi. Anda dapat menghapus ini di luar tryblok, tetapi kemudian memerlukan beberapa deteksi berantakan jika suatu variabel didefinisikan.


3
Apa yang salah dengan something = a[2]; print somethingbagian dalam coba: blokir?
S.Lott

@ S. Tidak punya apa-apa, tetapi bagaimana jika seseorang mengirimi Anda daftar, dan Anda tidak ingin menampilkan data jika tidak cukup lama karena mungkin rusak?
Tidak diketahui

12
S. Lott: 'cetak sesuatu' dapat menimbulkan pengecualian berbeda yang tidak ingin Anda sadap.
Darius Bacon

Saya tidak melihat perbedaannya. Jika saya mendapatkan pengecualian di luar batas, ia mencetak "di luar batas". Mengerti. Jika saya mendapatkan beberapa pengecualian lain, itu tidak tertangkap oleh blok kode ini. Jika saya tidak terkecuali, perilaku tersebut adalah mencetak nilai sesuatu, yang merupakan [2]. Saya tidak melihat apa yang dilakukan dalam contoh ini.
S.Lott

3
Nilai 'sesuatu', ketika dicetak, dapat meningkatkan kesalahan dalam metode __str __ (). Meskipun nilai itu sebenarnya hanya 2 dalam contoh ini, Anda mungkin juga menunjukkan bahwa tidak ada pengecualian di luar batas di sini juga.
Darius Bacon

8

Ada contoh yang bagus try-elsedalam PEP 380 . Pada dasarnya, ia turun untuk melakukan penanganan pengecualian yang berbeda di berbagai bagian algoritma.

Ini sesuatu seperti ini:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

Ini memungkinkan Anda untuk menulis kode penanganan pengecualian lebih dekat ke tempat pengecualian itu terjadi.


7

Dari Kesalahan dan Pengecualian # Menangani pengecualian - docs.python.org

The try ... exceptpernyataan memiliki opsional elseklausul, yang, ketika hadir, harus mengikuti semua kecuali klausa. Ini berguna untuk kode yang harus dijalankan jika klausa coba tidak menimbulkan pengecualian. Sebagai contoh:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

Penggunaan klausa lain lebih baik daripada menambahkan kode tambahan ke klausa coba karena menghindari secara tidak sengaja menangkap pengecualian yang tidak dimunculkan oleh kode yang dilindungi oleh percobaan ... kecuali pernyataan.


6

Melihat referensi Python tampaknya elsedijalankan setelah tryketika tidak ada pengecualian. Klausul opsional lain dijalankan jika dan ketika kontrol mengalir dari akhir klausa coba. 2 Pengecualian dalam klausa lain tidak ditangani oleh klausa sebelumnya kecuali.

Menyelam ke dalam python memiliki contoh di mana, jika saya mengerti dengan benar, dalam tryblok mereka mencoba mengimpor modul, ketika itu gagal Anda mendapatkan pengecualian dan mengikat default tetapi ketika berfungsi Anda memiliki pilihan untuk masuk ke elseblok dan mengikat apa yang diperlukan (lihat tautan untuk contoh dan penjelasan).

Jika Anda mencoba melakukan pekerjaan di catchblok itu mungkin melemparkan pengecualian lain - saya kira di situlah elseblok berguna.


4
"Pengecualian dalam klausa lain tidak ditangani oleh klausa sebelumnya kecuali." Itu bagian yang bermanfaat. Terima kasih.
geowa4

"Klausul opsional lain dijalankan jika dan ketika kontrol mengalir dari akhir klausa coba" adalah perbedaan lain, karena Anda dapat kembali keluar dari tryblok.
Tomer W

4

Itu dia. Blok 'lain' dari klausa coba-kecuali ada untuk kode yang berjalan ketika (dan hanya ketika) operasi yang dicoba berhasil. Itu bisa digunakan, dan bisa disalahgunakan.

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

Secara pribadi, saya suka dan menggunakannya saat yang tepat. Secara semantik mengelompokkan pernyataan.


2

Mungkin penggunaannya mungkin:

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

Mungkin ini akan membawa Anda terlalu berguna.


2

Saya telah menemukan try: ... else:konstruk yang berguna dalam situasi di mana Anda menjalankan kueri basis data dan mencatat hasil kueri tersebut ke basis data terpisah dengan rasa / jenis yang sama. Katakanlah saya memiliki banyak utas pekerja yang menangani semua kueri basis data yang dikirimkan ke antrian

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

Tentu saja jika Anda dapat membedakan antara kemungkinan pengecualian yang mungkin dilemparkan, Anda tidak harus menggunakan ini, tetapi jika kode bereaksi terhadap potongan kode yang berhasil mungkin melemparkan pengecualian yang sama dengan bagian yang berhasil, dan Anda tidak bisa hanya biarkan pengecualian kedua yang mungkin pergi, atau segera kembali pada kesuksesan (yang akan mematikan utas dalam kasus saya), maka ini berguna.


1

Sebuah elseblok sering dapat eksis untuk fungsi yang terjadi di setiap exceptblok.

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

Dalam hal ini, inconsistency_typedisetel di setiap blok kecuali, sehingga perilaku dilengkapi dalam kasus no-error di else.

Tentu saja, saya menggambarkan ini sebagai pola yang mungkin muncul dalam kode Anda sendiri suatu hari nanti. Dalam kasus khusus ini, Anda hanya menetapkan inconsistency_typeke 0 sebelum tryblok.


1

Berikut adalah tempat lain di mana saya suka menggunakan pola ini:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

1
Anda bisa menggunakan continuesaja - pola "break out early". Ini memungkinkan Anda untuk menjatuhkan klausa "lain" dan lekukannya, membuat kode lebih mudah dibaca.
malthe

1

Salah satu skenario penggunaan yang dapat saya pikirkan adalah pengecualian yang tidak dapat diprediksi, yang dapat dihindari jika Anda mencoba lagi. Misalnya, ketika operasi di blok uji coba melibatkan angka acak:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

Tetapi jika pengecualian dapat diprediksi, Anda harus selalu memilih validasi terlebih dahulu daripada pengecualian. Namun, tidak semuanya dapat diprediksi, sehingga pola kode ini memiliki tempatnya.


1
Anda dapat melakukan ini dengan meletakkan bagian breakdalam trydi bagian akhir, yang merupakan IMO bersih, dan Anda tidak memerlukannya else. Juga continuetidak benar-benar dibutuhkan, Anda bisa saja pass.
Dirbaio

1

Saya menemukan elseberguna untuk berurusan dengan file konfigurasi yang mungkin salah:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

Pengecualian yang membaca lockkonfigurasi menonaktifkan pemantauan kunci dan ValueErrors mencatat pesan peringatan yang bermanfaat.


1

Misalkan logika pemrograman Anda tergantung pada apakah kamus memiliki entri dengan kunci yang diberikan. Anda dapat menguji hasil dict.get(key)menggunakan if... else...konstruk, atau Anda dapat melakukan:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

-1

Saya akan menambahkan use case lain yang tampaknya lurus ke depan saat menangani sesi DB:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

-17

The else:blok membingungkan dan (hampir) tidak berguna. Itu juga bagian dari fordan whilepernyataan.

Sebenarnya, bahkan pada- ifpernyataan, else:dapat disalahgunakan dengan cara yang benar-benar mengerikan membuat bug yang sangat sulit ditemukan.

Pertimbangkan ini.

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

Pikirkan dua kali else:. Ini umumnya masalah. Hindari kecuali dalam ifpernyataan-dan bahkan kemudian mempertimbangkan mendokumentasikan else- kondisi untuk membuatnya eksplisit.


6
Saya tidak setuju dengan yang ini. Dalam "if-elif" blok, "else" digunakan sebagai "default" akan digunakan dalam "case" blok bahasa C. Selalu disarankan untuk menangani kasing "bawaan" walaupun Anda berpikir Anda telah membahas semua kasing dalam berbagai kondisi.
Josip

1
@Josip: digunakan sebagai "default" dapat membingungkan. Masalahnya adalah dengan jelas mendefinisikan kondisi "default" ini. Kondisi default yang tidak jelas dapat menjadi penyebab utama perilaku kereta. Yang lain bisa menjadi penyebab kebingungan. Ini harus dipikirkan dengan sangat hati-hati dalam semua kasus, tidak hanya mencoba, untuk dan sementara, tetapi jika juga.
S.Lott

5
Nah, kode di atas benar-benar abstrak dan tidak melakukan sesuatu yang berarti, jadi ya - tidak heran itu membingungkan.
julx

1
@ S.Lott "Itu akan mengurangi bugginess" - dan poin saya adalah bahwa ini salah. Saya pikir kita hanya memiliki perbedaan pendapat. Pemrogram yang buruk selalu menemukan cara untuk menulis program kereta. Selalu. Pemrogram yang baik selalu mencari praktik yang baik dan dapat menulis kode yang baik dalam hampir semua bahasa. Menghilangkan konstruk yang bermanfaat hanya memberikan sedikit daya untuk programmer yang baik sementara tidak secara khusus membantu yang buruk karena mereka mampu menemukan cara yang tidak terbatas untuk memperbaiki keadaan.
julx

5
Pertimbangkan: if x > 0: return "yes"dan if x <= 0: return "no". Sekarang seseorang datang dan mengubah salah satu syarat untuk mengatakan x > 1tetapi lupa untuk mengubah yang lain. Bagaimana itu mengurangi jumlah bug yang mau dilakukan. if elseklausa kadang-kadang banyak garis yang terpisah. KERING adalah praktik yang baik, jauh lebih sering daripada tidak, sungguh. (maaf untuk pos ganda).
julx
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.