Bileşik bir with
ifadeden kaynaklanan istisnaların olası kökenleri arasında ayrım yapma
Bir with
ifadede ortaya çıkan istisnalar arasında ayrım yapmak zordur, çünkü farklı yerlerde ortaya çıkabilirler. İstisnalar aşağıdaki yerlerden birinden (veya burada çağrılan işlevlerden) kaynaklanabilir:
ContextManager.__init__
ContextManager.__enter__
- gövdesi
with
ContextManager.__exit__
Daha fazla ayrıntı için Context Manager Türleri ile ilgili belgelere bakın .
Biz sadece sarma, bu farklı durumu ayırt etmek istiyorsanız with
bir içine try .. except
yeterli değildir. Aşağıdaki örneği düşünün (örnek ValueError
olarak kullanmak , ancak tabii ki başka bir istisna türüyle değiştirilebilir):
try:
with ContextManager():
BLOCK
except ValueError as err:
print(err)
Burada except
dört farklı yerin hepsinden kaynaklanan istisnaları yakalayacak ve böylece aralarında ayrım yapmaya izin vermeyecektir. Dışarı bağlam yöneticisi nesne örneğinin taşırsanız with
, biz ayırt edebilir __init__
ve BLOCK / __enter__ / __exit__
:
try:
mgr = ContextManager()
except ValueError as err:
print('__init__ raised:', err)
else:
try:
with mgr:
try:
BLOCK
except TypeError: # catching another type (which we want to handle here)
pass
except ValueError as err:
# At this point we still cannot distinguish between exceptions raised from
# __enter__, BLOCK, __exit__ (also BLOCK since we didn't catch ValueError in the body)
pass
Etkili bir şekilde bu sadece __init__
parçaya yardımcı oldu, ancak with
yürütmeye başlayan gövdenin (yani __enter__
ve diğerleri arasında ayrım yapma ) olup olmadığını kontrol etmek için ekstra bir sentinel değişkeni ekleyebiliriz :
try:
mgr = ContextManager() # __init__ could raise
except ValueError as err:
print('__init__ raised:', err)
else:
try:
entered_body = False
with mgr:
entered_body = True # __enter__ did not raise at this point
try:
BLOCK
except TypeError: # catching another type (which we want to handle here)
pass
except ValueError as err:
if not entered_body:
print('__enter__ raised:', err)
else:
# At this point we know the exception came either from BLOCK or from __exit__
pass
Zor kısım, ortaya çıkan istisnalar arasında ayrım yapmaktır BLOCK
ve __exit__
çünkü nasıl ele alınacağına karar verebilecek with
iradenin gövdesinden kaçan bir istisna geçirilir __exit__
( dokümanlara bakın ). Ancak __exit__
kendini yükseltirse, orijinal istisna yenisiyle değiştirilecektir. Bu davalarla başa çıkmak için, fark edilmeden kaçabilecek herhangi bir potansiyel istisnayı depolamak ve daha sonra en dışta yakalananla karşılaştırmak except
için - vücudun genel bir cümlesini ekleyebiliriz - eğer aynıysa, menşe ya da aksi halde ( en dıştaki gerçek değeri döndürerek istisnayı bastırması durumunda)with
except
BLOCK
__exit__
__exit__
except
basitçe yürütülmez).
try:
mgr = ContextManager() # __init__ could raise
except ValueError as err:
print('__init__ raised:', err)
else:
entered_body = exc_escaped_from_body = False
try:
with mgr:
entered_body = True # __enter__ did not raise at this point
try:
BLOCK
except TypeError: # catching another type (which we want to handle here)
pass
except Exception as err: # this exception would normally escape without notice
# we store this exception to check in the outer `except` clause
# whether it is the same (otherwise it comes from __exit__)
exc_escaped_from_body = err
raise # re-raise since we didn't intend to handle it, just needed to store it
except ValueError as err:
if not entered_body:
print('__enter__ raised:', err)
elif err is exc_escaped_from_body:
print('BLOCK raised:', err)
else:
print('__exit__ raised:', err)
PEP 343'te belirtilen eşdeğer formu kullanarak alternatif yaklaşım
PEP 343 - "with" Deyimi , with
deyimin eşdeğer bir "birlikte olmayan" sürümünü belirtir . Burada çeşitli parçaları kolayca try ... except
farklı potansiyel hata kaynaklarıyla sarabilir ve böylece birbirinden ayırabiliriz :
import sys
try:
mgr = ContextManager()
except ValueError as err:
print('__init__ raised:', err)
else:
try:
value = type(mgr).__enter__(mgr)
except ValueError as err:
print('__enter__ raised:', err)
else:
exit = type(mgr).__exit__
exc = True
try:
try:
BLOCK
except TypeError:
pass
except:
exc = False
try:
exit_val = exit(mgr, *sys.exc_info())
except ValueError as err:
print('__exit__ raised:', err)
else:
if not exit_val:
raise
except ValueError as err:
print('BLOCK raised:', err)
finally:
if exc:
try:
exit(mgr, None, None, None)
except ValueError as err:
print('__exit__ raised:', err)
Genellikle daha basit bir yaklaşım iyi olur
Bu tür özel istisna muamelesine duyulan ihtiyaç oldukça nadir olmalı ve normalde with
bir try ... except
bloğa normal olarak sarılması yeterli olacaktır. Özellikle çeşitli hata kaynakları farklı (özel) istisna türleriyle (içerik yöneticilerinin buna göre tasarlanması gerekir) belirtilirse, bunlar arasında kolayca ayrım yapabiliriz. Örneğin:
try:
with ContextManager():
BLOCK
except InitError: # raised from __init__
...
except AcquireResourceError: # raised from __enter__
...
except ValueError: # raised from BLOCK
...
except ReleaseResourceError: # raised from __exit__
...
with
ifade, çevredeki birtry...except
ifadeyi sihirli bir şekilde bozmaz .