2017-07-25 42 views
6

Yakalanan istisnayı bağlamak için bu ad kullanıldığında, Python'un bir ad bağlamasının silinmesini nasıl engelleyebilirim? davranışındaki bu değişiklik Python'a ne zaman geldi?`except` cümlecikindeki ad ekleme yan tümcesinden sonra silindi

Python 2 ve Python 3 hem çalıştırmak için kod yazıyorum: Python dış kapsamda bir isimdir bilmesi için açıkça önce istisna işleme bağlı olduğu

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

Bildirimi exc söyledi.

Python 2.7 üzerinde

, bu güzel çalışır ve exc adı kullanılacak hayatta format çağrı ::

Got exception: ZeroDivisionError 

Büyük, bu benim istediğim tam olarak ne: except fıkra adını bağlar ve istisna nesnesine başvurmak için işlevin geri kalanında bu adı kullanabilirim. görünüşe exc ::

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

Neden exc dış kapsamdan silinir bağlayıcı olduğunu silinir bağlanma nedeniyle

Python 3.5 üzerinde

, format çağrı başarısız? maddesinden sonra kullanmak üzere ad bağlama özelliğini güvenilir şekilde korumak için kastediyoruz?

Bu değişiklik Python'a ne zaman geldi, nerede belgelendi?

Bunu Python 3'te bir hata olarak bildirme hakkım olur mu?

+0

: try/except kodu için sökme de açıkça belgeler ile yapılan yukarıdaki ifadeleri destekler. 'Except' cümlesi içinde başka bir değişkene' exc 'atarsanız, oraya alabilirsiniz. Olası bir kopya https://stackoverflow.com/questions/29268892/python-3-exception-deletes-variable-in-enclosing-scope-for-unknown-reason Bu cevabı yapmam gerektiğini biliyordum ... lol –

cevap

6

Hayır, bu bir hata değildir. Karşılaştığınız davranış açıkça ve açıkça Python 3 documentation for the try/except statement'da tanımlanmıştır. Bu davranışın nedeni de verilir:

bir istisna as target kullanılarak atanmıştır, bu except maddesinin sonunda silinir.

except E as N: 
    foo 

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

çevrilen bu istisna except fıkra sonra programa başvurmak edebilmek için farklı bir isim atanmalıdır anlamına gelir budur. İstisnalar temizlenir, çünkü bunlara iliştirilmiş traceback ile, bir sonraki çöp toplama işlemi meydana gelinceye kadar tüm yerlileri o çerçevede canlı tutarak yığın çerçevesiyle bir referans döngüsü oluştururlar. Eğer as fıkrada exc kullanılan çünkü

işe yaramadı try/except bloğunun kapsamı dışında adını bildirerek nedenidir. Bu yüzden Python adı silindi.

düzeltme istisna bağlanmasına as fıkrada farklı bir ad kullanın ve daha sonra farklı bir istisna adına küresel değişkeni atamak için:

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

Anthony Sottile açıklamalarda belirtildiği gibi, Bu amaçlanan görünüyor

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

bunun demontajı da oldukça açıktır (bir fonksiyona atmak, dis.dis (f) 'yi çalıştırmak) - esas olarak' exc = None; Sonunda 'son' bloğu –

+1

@AnthonySottile Great point. Bunu cevabıma ekleyeceğim. –