이제 파이썬에 새로운 것이 계속 들어와도 놀라지 않을 만큼, 파이썬은 끊임없이 새로운 문법이 매 버전마다 들어오고 있습니다. 파이썬 2.3에서 generator/bool, 2.4에서 generator expression이 하이라이트였다면, 2.5에서는 단연 PEP-343 with 절이 가장 주요한 문법의 변화가 되겠습니다.
with 절은 이미 다른 언어에 많이 소개가 되어 있는 개념을 구현하기 위한 것인데, ruby의 block이나 자바의 synchronized 같은 것들과 비슷한 개념을 지원합니다. 그렇지만, 비주얼 베이식의 with같이 네임스페이스를 줄이는 목적으로 쓰는 것은 아니기 때문에 서로 다릅니다.
with 절은 원래 PEP-340 Anonymous Block Statements에서 소개되었던 문맥 처리 기능들을 PEP-340이 몇가지 문제로 인해서 거부되자 대체 문법으로 등장한 것입니다. with 절의 문법은 이렇습니다.
1 2 |
with EXPR as VAR: BLOCK |
as VAR 부분은 생략이 가능합니다. 이렇게 쓰면 내부적으로 다음과 같이 번역이 되어서 실행이 되게 됩니다. (소문자로 된 변수들은 VM 내부적인 변수이므로 소스코드에서는 접근 가능하지 않습니다.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
ctx = (EXPR).__context__() exit = ctx.__exit__ # 아직 호출하지는 않음 value = ctx.__enter__() exc = True try: try: VAR = value # "as VAR"이 있을 경우에만 BLOCK except: # 여기서 예외 처리를 함 exc = False exit(*sys.exc_info()) finally: # 지역적이지 않은 분기는 여기서 처리함 if exc: exit(None, None, None) |
번역 부분을 약간 살펴보면 새로운 메쏘드 이름인 __context__, __exit__, __enter__를 쓰고 있습니다.
with x as y: 라고 쓰면 x.__context__()를 호출한 다음,
그 녀석이 리턴한 객체의 __enter__()를 호출해서 리턴된 것을
y에 넣어줍니다. 단, with절은 블럭 안에서 프레임이 생성되지 않기 때문에
y의 유효영역은 with절 안이 아니라, 외부의 지역 네임스페이스입니다. (밑줄 쫙~)
자.. 과연 이런게 어디에 쓸모가 있을까! 생각해 보면, 당장 생각 나는 것은 임시 파일을 생성했을 때 귀찮게 finally로 감싸서 파일 지워주는 경우가 생각납니다. 그런데, 이런 사용 사례들을 일일이 위와 같이 __context__, __enter__같은 것을 다 구현해 주기는 굉장히 귀찮기 때문에, 실제로는 제너레이터로 간단하게 구현할 수 있도록 contextlib이라는 모듈이 신설되었습니다. contextlib에는 contextmanager라는 데코레이터가 있어서, 제너레이터를 구현할 때 @contextmanager를 통과시켜 주면 쉽게 with용 컨텍스트매니저로 변신을 시켜 줍니다. closing이라는 것도 있어서 with를 벗어나면 자동으로 파일 닫게도 할 수도 있고요~
한 번 시험해 보기 위해서, 대표적인 with절 예상 사용 사례인 SQL 데이터베이스의 트랜잭션 처리 부분을 한번 해 봤습니다. 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from __future__ import with_statement from contextlib import contextmanager import MySQLdb @contextmanager def mysqlconn(hostname, username, password, database): conn = MySQLdb.connect(hostname, username, password, database) curs = conn.cursor() try: curs.execute('BEGIN') yield curs except: curs.execute('ROLLBACK') raise else: curs.execute('COMMIT') finally: conn.close() with mysqlconn('localhost', 'user', 'passwd', 'db') as curs: curs.execute('select count(*) from schedule') print curs.fetchone() |
with절에 진입하면서 자동으로 데이터베이스 접속을 맺어주며, 안에서 예외가 발생하면 롤백하고, 예외가 발생하지 않고 빠져나오면 자동으로 커밋하게 해 줍니다. 이와 비슷하게 threading.Queue같이 쓰레드 간 동기화나 임시 객체가 생성되는 온갖 사용처에 아주 유용하게 쓰일 수 있을 듯 합니다~ (위 예제를 유심히 보시면 with절 외에도 2.4에서는 안 되던 문법이 2가지 숨겨져 있습니다. 이히히히~)
그런데, with는 새로운 키워드이기 때문에, 2.5에서는 from __future__ import with_statement를 해야지만 사용할 수 있고, 2.6부터는 정규 키워드로 지정될 예정입니다.
자, 이제 파이썬이 프로그래밍을 안 해본 사람도 하루만에 배울 수 있는 언어라는 굴레에서 해방되었습니다. -O-;;;;;
예제에 나온 mysqlconn의 정의만 봐서는, 어떻게 마법이 생기는지 모르겠는데, @contextmanager가 마법을 부리는건가요?
contextlib 소스 (http://pxr.openlook.org/pxr/source/Lib/contextlib.py#037)
에 보시면 함수 정의가 있는데, 저놈이 GeneratorContextManager 클래스에, 들어온 함수를 씌워버려요~