Tại sao việc sử dụng `or` trong một mệnh đề ngoại trừ không gây ra SyntaxError? Có sử dụng hợp lệ cho nó?


11

Trong công việc, tôi tình cờ thấy một exceptđiều khoản với một ornhà điều hành:

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

Tôi biết các lớp ngoại lệ nên được thông qua như một tuple, nhưng nó đã nói với tôi rằng nó thậm chí sẽ không gây ra a SyntaxError.

Vì vậy, trước tiên tôi muốn điều tra xem nó thực sự hoạt động. Và nó không.

>>> 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

Vì vậy, nó đã không bắt được ngoại lệ thứ hai và nhìn vào mã byte, nó trở nên rõ ràng hơn tại sao:

>>> 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

Vì vậy, chúng ta có thể thấy, hướng dẫn 14 đầu tiên tải IndexErrorlớp lên ngăn xếp. Sau đó, nó kiểm tra xem giá trị đó có phải không True, đó là do tính trung thực của Python và cuối cùng nhảy trực tiếp đến hướng dẫn 20 nơi exception matchthực hiện. Vì hướng dẫn 18 đã bị bỏ qua, KeyErrorkhông bao giờ được tải lên ngăn xếp và do đó không khớp.

Tôi đã thử với Python 2.7 và 3.6, kết quả tương tự.

Nhưng sau đó, tại sao nó là cú pháp hợp lệ? Tôi tưởng tượng nó là một trong những điều sau đây:

  1. Đây là một tạo tác từ một phiên bản Python thực sự cũ.
  2. Thực sự có một trường hợp sử dụng hợp lệ để sử dụng ortrong một exceptmệnh đề.
  3. Nó chỉ đơn giản là một giới hạn của trình phân tích cú pháp Python có thể phải chấp nhận bất kỳ biểu thức nào sau excepttừ khóa.

Phiếu bầu của tôi là 3 (được cho là tôi đã thấy một số cuộc thảo luận về trình phân tích cú pháp mới cho Python) nhưng tôi hy vọng ai đó có thể xác nhận giả thuyết đó. Bởi vì nếu là 2 chẳng hạn, tôi muốn biết trường hợp sử dụng đó!

Ngoài ra, tôi có một chút không biết về cách tôi tiếp tục cuộc thám hiểm đó. Tôi tưởng tượng rằng tôi sẽ phải đào sâu vào mã nguồn của trình phân tích cú pháp CPython nhưng idk tìm ở đâu và có thể có một cách dễ dàng hơn?

Câu trả lời:


7

Trong except e, ecó thể là bất kỳ biểu thức Python hợp lệ nào:

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

[..] Đối với một exceptmệnh đề có biểu thức, biểu thức đó được ước tính và mệnh đề khớp với ngoại lệ nếu đối tượng kết quả là Tương thích trực tiếp với ngoại lệ. Một đối tượng tương thích với một ngoại lệ nếu đó là lớp hoặc lớp cơ sở của đối tượng ngoại lệ hoặc một bộ chứa một mục tương thích với ngoại lệ.

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

Biểu thức IndexError or KeyErrormang lại giá trị IndexError. Vì vậy, điều này tương đương với:

except IndexError:
   ...

Cảm ơn bạn đã trả lời nhanh chóng ánh sáng! Chúng tôi sẽ coi đó là một giới hạn của trình phân tích cú pháp (nghĩa là chỉ có thể chấp nhận bất kỳ biểu thức nào thay vì một biểu thức cụ thể hơn) hoặc một lựa chọn có chủ ý để không hạn chế mọi thứ quá nhiều?
Loïc Teixeira

1
Tôi dường như tuân theo các nguyên tắc cơ bản của Python là đơn giản hơn. Tại sao phát minh ra các quy tắc mới chỉ có thể giới hạn, khi chấp nhận bất kỳ biểu thức nào có nghĩa là hoàn toàn tự do mà không có trường hợp đặc biệt mới?
lừa dối

Đó là một sự lựa chọn có chủ ý, cho phép một người lắp ráp một bộ ngoại lệ để nắm bắt một cách linh hoạt và sử dụng một giá trị như vậy trong một excepttuyên bố.
dùng4815162342

Tôi đoán lý do giới hạn các khả năng sẽ ngăn các nhà phát triển viết mã mà không làm những gì họ dự định. Rốt cuộc, viết có except IndexError or KeyErrorvẻ như là một điều tốt để viết. Tuy nhiên tôi đồng ý với bạn rằng nó sẽ chống lại một số giá trị khác mà Python cố gắng tôn trọng.
Loïc Teixeira

Lưu ý rằng trăn cũng cho phép var == 1 or 2, điều mà đối với mắt chưa được huấn luyện cũng "trông giống như một điều tốt để viết".
Eric

-2

Bạn nên sử dụng n-tuple các loại thay vì biểu thức logic (chỉ trả về phần tử không sai đầu tiên):

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

Như đã nêu trong câu hỏi, tôi biết các lớp ngoại lệ nên được thông qua dưới dạng tuple nhưng tôi đã tự hỏi tại sao sử dụng orvẫn là Python hợp lệ.
Loïc Teixeira
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.