Programming Language/Python3

Python3 with catch

아기 요다 2021. 10. 25. 19:01

with statement

python에서 with문을 사용하는 것은 파일을 읽을 때, 장점이 있다.

 

with open("./note.txt") as f:
    text = f.read()
    # f.close()

 

with문이 종료할 때 별도로 .close()를 하지 않아도 종료해주기 때문에 안전하게 파일에 접근할 수 있다.
그렇다면 내 생각엔 with문으로 파일을 읽는 동안 발생하는 에러에 대해서 with except문같은 구문이 있지 않을까 생각했다.

 


결론

결론부터 말하면, 그러한 문법은 존재하지 않았다.
아래의 답변은 with문을 try로 감싸거나, 파일을 열 때 try로 감싸고 그 후 에러가 없는 경우 with를 쓰는 코드를 보여주고 있다.

  1. try-with
from __future__ import with_statement

try:
    with open( "a.txt" ) as f :
        print f.readlines()
except EnvironmentError: # parent of IOError, OSError *and* WindowsError where available
    print 'oops'
  1. try open-else with
try:
    f = open('foo.txt')
except IOError:
    print('error')
else:
    with f:
        print f.readlines()

 

내 결론

난 위의 방법이 아닌 그저 with문을 안쓰는 쪽을 선택했다.

 


 

Deep dive

그런데 아직도 이상하다. 왜 지원해주지 않는걸까?


stackoverflow catching-an-exception-while-using-a-python-with-statement에서 a_guest의 답변에 따르면 ContextManager때문이라고 한다.

 

자세한 내용까지는 다 이해하지 못했지만, 개략적으로 이야기하자면 ContextManager의 동작방식때문이라고 한다. PEP 343의 예시에 따르면 아래의 코드와 with문이 동일한 형태라고 한다.

 

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)

 

코드에서는 아래와 같은 경우에서 raise가 발생한다. (코드에서는 print로 표시되어 있다.)

  • ContextManager.__init__
  • ContextManager.__enter__
  • the body of the with
  • ContextManager.__exit__

 

with-except를 쓸 수 없는 이유는 raise가 여러 곳에서 발생하기 때문이라고 이해했다.
물론 except로 발생하는 모든 예외를 잡을 수 있을 것이다. 하지만 그것은 잘못된 표현법이다.


왜냐하면 외부에서 봤을 때 with문은 한줄이기 때문에 하나의 뎁스?의 코드가 동작하고 반환되는 것 같지만 실제로는 여러 뎁스의 동작이 존재하기 때문에 명시적으로 with-except문을 사용하게 되면, 사용자는 with에서 발생한 에러에 대해 추상적으로 판단하게 될 것이다.

 

어쨌든 with문은 굉장히 추상화되어 있는 구문이다.
좋은 표현법이라고 하기 어렵겠다.

 


References