Mengapa menggunakan `atau` dalam klausa kecuali tidak menyebabkan SyntaxError? Apakah ada penggunaan yang valid untuk itu?

11

Di tempat kerja, saya menemukan exceptklausa dengan oroperator:

try:
    # Do something.
except IndexError or KeyError:
    # ErrorHandling

Saya tahu kelas pengecualian harus lulus sebagai tuple, tapi itu menyadap saya bahwa itu bahkan tidak akan menyebabkan a SyntaxError.

Jadi pertama saya ingin menyelidiki apakah itu benar-benar berfungsi. Dan ternyata tidak.

>>> def with_or_raise(exc):
...     try:
...         raise exc()
...     except IndexError or KeyError:
...         print('Got ya!')
...

>>> with_or_raise(IndexError)
Got ya!

>>> with_or_raise(KeyError)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in with_or_raise
KeyError

Jadi itu tidak menangkap pengecualian kedua, dan melihat bytecode, menjadi lebih jelas mengapa:

>>> import dis
>>> dis.dis(with_or_raise)
  2           0 SETUP_EXCEPT            10 (to 12)

  3           2 LOAD_FAST                0 (exc)
              4 CALL_FUNCTION            0
              6 RAISE_VARARGS            1
              8 POP_BLOCK
             10 JUMP_FORWARD            32 (to 44)

  4     >>   12 DUP_TOP
             14 LOAD_GLOBAL              0 (IndexError)
             16 JUMP_IF_TRUE_OR_POP     20
             18 LOAD_GLOBAL              1 (KeyError)
        >>   20 COMPARE_OP              10 (exception match)
             22 POP_JUMP_IF_FALSE       42
             24 POP_TOP
             26 POP_TOP
             28 POP_TOP

  5          30 LOAD_GLOBAL              2 (print)
             32 LOAD_CONST               1 ('Got ya!')
             34 CALL_FUNCTION            1
             36 POP_TOP
             38 POP_EXCEPT
             40 JUMP_FORWARD             2 (to 44)
        >>   42 END_FINALLY
        >>   44 LOAD_CONST               0 (None)
             46 RETURN_VALUE

Jadi bisa kita lihat, instruksi 14 pertama memuat IndexErrorkelas ke stack. Kemudian memeriksa apakah nilai itu True, yang karena kebenaran Python dan akhirnya melompat langsung ke instruksi 20 di mana exception matchdilakukan. Karena instruksi 18 dilewati, KeyErrortidak pernah dimuat ke tumpukan dan karenanya tidak cocok.

Saya mencoba dengan Python 2.7 dan 3.6, hasilnya sama.

Tetapi, mengapa sintaksis itu valid? Saya membayangkannya sebagai salah satu dari yang berikut:

  1. Ini adalah artefak dari versi Python yang sangat lama.
  2. Sebenarnya ada use case yang valid untuk digunakan ordalam exceptklausa.
  3. Ini hanyalah batasan dari pengurai Python yang mungkin harus menerima ekspresi apa pun setelah exceptkata kunci.

Suara saya ada di 3 (mengingat saya melihat beberapa diskusi tentang parser baru untuk Python) tapi saya berharap seseorang dapat mengkonfirmasi hipotesis itu. Karena kalau itu 2 misalnya, saya ingin tahu use case itu!

Juga, saya agak tidak mengerti tentang bagaimana saya melanjutkan eksplorasi itu. Saya membayangkan saya harus menggali kode sumber CPython parser tetapi idk di mana menemukannya dan mungkin ada cara yang lebih mudah?

Loix Teixeira
sumber

Jawaban:

7

Di except e, edapat berupa ekspresi Python yang valid:

try1_stmt ::=  "try" ":" suite
               ("except" [expression ["as" identifier]] ":" suite)+
               ...

[..] Untuk exceptklausa dengan ekspresi, ekspresi itu dievaluasi, dan klausa cocok dengan pengecualian jika objek yang dihasilkan "kompatibel" dengan pengecualian. Objek kompatibel dengan pengecualian jika itu adalah kelas atau kelas dasar dari objek pengecualian atau tuple yang berisi item yang kompatibel dengan pengecualian.

https://docs.python.org/3/reference/compound_stmts.html#the-try-statement

Ekspresi IndexError or KeyErrormenghasilkan nilai IndexError. Jadi ini setara dengan:

except IndexError:
   ...
tipuan
sumber
Terima kasih atas jawaban cepat pencahayaan itu! Akankah kita menganggapnya sebagai batasan parser (yaitu hanya bisa menerima ekspresi apa pun daripada yang lebih spesifik) atau pilihan yang disengaja agar tidak terlalu membatasi hal-hal?
Loïc Teixeira
1
Tampaknya bagi saya untuk berpegang pada prinsip-prinsip dasar Python sederhana menjadi lebih baik. Mengapa menemukan aturan baru yang hanya bisa membatasi, ketika menerima ungkapan apa pun berarti kebebasan penuh tanpa kasus khusus baru?
tipuan
Ini adalah pilihan yang disengaja, memungkinkan seseorang untuk mengumpulkan tuple pengecualian untuk ditangkap secara dinamis, dan menggunakan menggunakan nilai seperti itu dalam exceptpernyataan.
user4815162342
Saya kira alasan untuk membatasi kemungkinan akan mencegah pengembang dari menulis kode yang tidak melakukan apa yang mereka maksudkan. Bagaimanapun, menulis except IndexError or KeyErrorsepertinya hal yang layak untuk ditulis. Namun saya setuju dengan Anda bahwa itu akan bertentangan dengan beberapa nilai lain yang Python coba hormati.
Loïc Teixeira
Perhatikan bahwa python juga memungkinkan var == 1 or 2, yang bagi mata yang tidak terlatih juga "terlihat seperti hal yang layak untuk ditulis".
Eric
-2

Anda harus menggunakan n-tupel jenis alih-alih ekspresi logis (yang hanya mengembalikan elemen non-salah pertama):

def with_or_raise(exc):
  try:
    raise exc()
  except (IndexError,KeyError):
    print('Got ya!')
pengguna2622016
sumber
Seperti yang dinyatakan dalam pertanyaan, saya tahu kelas pengecualian harus diteruskan sebagai tuple tetapi saya bertanya-tanya mengapa menggunakan orPython masih valid.
Loïc Teixeira