복합 with
문장 에서 발생하는 예외의 가능한 원인을 구별
with
명령문 에서 발생하는 예외를 구별하는 것은 다른 위치에서 발생할 수 있으므로 까다 롭습니다. 다음 장소 중 하나 (또는 여기에서 호출되는 함수)에서 예외가 발생할 수 있습니다.
ContextManager.__init__
ContextManager.__enter__
- 몸
with
ContextManager.__exit__
자세한 내용은 컨텍스트 관리자 유형 에 대한 설명서를 참조하십시오 .
이러한 서로 다른 경우를 구별하려면 with
을 감싸는 것만 으로 try .. except
는 충분하지 않습니다. 다음 예제를 고려하십시오 (예를 사용 ValueError
하지만 다른 예외 유형으로 대체 할 수 있음).
try:
with ContextManager():
BLOCK
except ValueError as err:
print(err)
여기서는 except
네 곳의 모든 장소에서 발생하는 예외를 포착 하므로 예외를 구분할 수 없습니다. 우리는 외부 상황에 맞는 관리자 개체의 인스턴스를 이동하는 경우 with
, 우리는 구별 할 수 __init__
및 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
실제로 이것은 __init__
부분적으로 도움이 되었지만 추가 센티넬 변수를 추가하여 with
시작된 본문이 실행 되는지 여부를 확인할 수 있습니다 (예 : 다른 것과 구별 __enter__
).
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
까다로운 부분은 예외 발신 구별한다 BLOCK
과 __exit__
가 본체의 탈출 때문에 예외 with
로 전달한다 __exit__
(참조를 처리하는 방법을 결정할 수있는 워드 프로세서 ). 그러나 __exit__
자체적으로 발생하면 원래 예외는 새로운 예외로 대체됩니다. 이러한 경우를 처리하기 위해 우리는 except
본문에 일반 조항을 추가하여 with
눈에 띄지 않게 탈출했을 가능성이있는 예외를 저장하고 except
나중에 가장 바깥 쪽에서 잡은 예외와 비교할 수 있습니다. BLOCK
또는 그렇지 않으면 __exit__
( __exit__
가장 바깥쪽에 true 값을 반환하여 예외를 억제하는 경우)except
단순히 실행되지 않습니다).
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에 언급 된 동등한 양식을 사용하는 대체 접근법
PEP 343- "with"문 은 동등한 "non-with"버전의 with
문장을 지정합니다. 여기서 우리는 다양한 부분을 쉽게 감싸서 try ... except
다른 잠재적 오류 소스를 구별 할 수 있습니다.
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)
일반적으로 간단한 접근 방식은 잘 작동합니다.
이러한 특별한 예외 처리의 필요성은 매우 드물며 일반적으로 전체 with
를 try ... except
블록 으로 감싸는 것으로 충분합니다. 특히 다양한 오류 소스가 다른 (사용자 정의) 예외 유형으로 표시되는 경우 (컨텍스트 관리자를 적절히 설계해야 함)이를 쉽게 구분할 수 있습니다. 예를 들면 다음과 같습니다.
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
문은 마술 주변 중단하지 않는try...except
문.