2017-07-25 13 views
6

Làm cách nào để ngăn chặn việc xóa bỏ ràng buộc tên của Python, khi tên đó là được sử dụng để ràng buộc ngoại lệ bị bắt? Khi nào thì thay đổi này trong hành vi có trong Python?Ràng buộc tên trong mệnh đề `except` bị xóa sau mệnh đề

Tôi viết mã để chạy trên cả hai Python 2 và Python 3:

exc = None 
try: 
    1/0 
    text_template = "All fine!" 
except ZeroDivisionError as exc: 
    text_template = "Got exception: {exc.__class__.__name__}" 

print(text_template.format(exc=exc)) 

ý rằng exc là ràng buộc rõ ràng trước việc xử lý ngoại lệ, vì vậy Python biết nó là một cái tên trong phạm vi bên ngoài.

Mở Python 2.7, này chạy tốt và tên exc sống sót được sử dụng trong các format gọi ::

Got exception: ZeroDivisionError 

Tuyệt vời, đây chính xác là những gì tôi muốn: Mệnh đề except liên kết tên và Tôi có thể sử dụng tên đó trong phần còn lại của hàm để tham chiếu đến đối tượng ngoại lệ .

Mở Python 3.5, cuộc gọi format thất bại bởi vì rõ ràng là exc ràng buộc được xóa ::

Traceback (most recent call last): 
    File "<stdin>", line 8, in <module> 
NameError: name 'exc' is not defined 

Tại sao exc ràng buộc xóa khỏi phạm vi bên ngoài? Làm cách nào để chúng tôi có nghĩa là để bảo vệ tên ràng buộc một cách đáng tin cậy khi sử dụng nó sau khi các yêu cầu except ?

Khi nào thay đổi này được đưa vào Python, tài liệu được viết ở đâu?

Tôi có quyền báo cáo điều này dưới dạng lỗi trong Python 3 không?

+0

Điều này có vẻ như mong muốn. Nếu bạn gán 'exc' cho một biến khác trong mệnh đề' except', bạn có thể lấy nó ở đó. Có thể sao chép https://stackoverflow.com/questions/29268892/python-3-exception-deletes-variable-in-enclosing-scope-for-unknown-reason Tôi biết tôi nên đã thực hiện câu trả lời này ... lol –

Trả lời

6

Không có lỗi này. Hành vi bạn đang gặp phải được xác định rõ ràng và rõ ràng trong Python 3 documentation for the try/except statement. Lý do cho hành vi này cũng được đưa ra:

Khi một ngoại lệ đã được chỉ định sử dụng as target, nó sẽ bị xóa vào cuối mệnh đề except. Đây là như thể

except E as N: 
    foo 

được dịch sang

except E as N: 
    try: 
     foo 
    finally: 
     del N 

này có nghĩa là ngoại lệ phải được gán cho một tên khác nhau để có thể đề cập đến nó sau khi mệnh đề except. Trường hợp ngoại lệ được xóa bởi vì truy nguyên được gắn với chúng, chúng tạo thành một chu trình tham chiếu với khung ngăn xếp, giữ tất cả người dân trong khung đó còn sống cho đến khi bộ sưu tập rác tiếp theo xuất hiện.

Lý do tuyên bố tên ngoài phạm vi của khối try/except đã không làm việc là bởi vì bạn sử dụng exc trong mệnh đề as. Vì vậy, đó là tên Python đã bị xóa.

Việc sửa chữa là sử dụng một tên khác nhau trong mệnh đề as để ràng buộc các trường hợp ngoại lệ, và sau đó gán các biến toàn cầu với tên ngoại lệ khác nhau:

>>> exc_global = None 
>>> try: 
    1/0 
    text_template = "All fine!" 
except ZeroDivisionError as exc: 
    exc_global = exc 
    text_template = "Got exception: {exc.__class__.__name__}" 


>>> print(text_template.format(exc=exc_global)) 
Got exception: ZeroDivisionError 

Như Anthony Sottile lưu ý trong các ý kiến, tháo gỡ cho mã số try/except cũng hỗ trợ rõ ràng các tuyên bố trên được thực hiện bởi tài liệu:

>>> code = """ 
try: 
    1/0 
    text_template = "All fine!" 
except ZeroDivisionError as exc: 
    text_template = "Got exception: {exc.__class__.__name__}" 
""" 
>>> from dis import dis 
>>> dis(code) 
    2   0 SETUP_EXCEPT   16 (to 18) 

    3   2 LOAD_CONST    0 (1) 
       4 LOAD_CONST    1 (0) 
       6 BINARY_TRUE_DIVIDE 
       8 POP_TOP 

    4   10 LOAD_CONST    2 ('All fine!') 
      12 STORE_NAME    0 (text_template) 
      14 POP_BLOCK 
      16 JUMP_FORWARD   38 (to 56) 

    5  >> 18 DUP_TOP 
      20 LOAD_NAME    1 (ZeroDivisionError) 
      22 COMPARE_OP    10 (exception match) 
      24 POP_JUMP_IF_FALSE  54 
      26 POP_TOP 
      28 STORE_NAME    2 (exc) 
      30 POP_TOP 
      32 SETUP_FINALLY   10 (to 44) 

    6   34 LOAD_CONST    3 ('Got exception: {exc.__class__.__name__}') 
      36 STORE_NAME    0 (text_template) 
      38 POP_BLOCK 
      40 POP_EXCEPT 
      42 LOAD_CONST    4 (None) 
     >> 44 LOAD_CONST    4 (None) 
      46 STORE_NAME    2 (exc) 
      48 DELETE_NAME    2 (exc) 
      50 END_FINALLY 
      52 JUMP_FORWARD    2 (to 56) 
     >> 54 END_FINALLY 
     >> 56 LOAD_CONST    4 (None) 
      58 RETURN_VALUE 
+0

disassembly của điều này là khá rõ ràng quá (ném nó trong một chức năng, chạy 'dis.dis (f)') - nó cho biết thêm về cơ bản 'exc = None; del exc' đến cuối 'cuối cùng 'khối –

+1

@AnthonySottile Điểm tuyệt vời. Tôi sẽ thêm nó vào câu trả lời của tôi. –

Các vấn đề liên quan