기초
컴퓨터에서 사용할 수 있는 리소스는 제한적입니다. 따라서 사용한(acquired) 리소스는 종료해주는 것(released)이 중요합니다. 그렇지 않다면 프로그램이 종료된 이후에도 사용한 리소스가 계속 열려있는 resource leak
현상이 발생합니다.
f = open('memo.txt', 'r')
print(f.read())
위의 코드처럼 파일을 열고 닫아주지 않는다면, 파일이 계속 컴퓨터에 남아있을 확률이 있습니다. (대부분 Python이 종료해준다고 하긴 합니다.)
>> Hello, World
따라서 아래와 같이 열었던 파일을 닫아주어야 합니다.
f = open('memo.txt', 'r')
print(f.read())
f.close()
하지만, 코드가 복잡해지다보면 파일을 닫기전에 에러가 발생할 가능성이 높아집니다.
f = open('memo.txt', 'r')
...
print(f.read())
... (예외발생)
f.close() #실행되지 않음
try
, (except)
, finally
구문을 사용하면 이 문제를 쉽게 해결할 수 있습니다.
f = open('memo.txt', 'r')
try:
...
print(f.read())
... (예외발생)
finally:
f.close() # 예외가 발생하더라도 실행됨
이렇게 try
와 finally
를 활용할 수도 있지만, with
를 이용하면 더욱 간단하게 구현할 수 있습니다. with문의 범위를 벗어날 때, 혹은 with문 내에서 예외가 발생하더라도 파일 종료를 보장해줍니다.
with open('memo.txt', 'r') as f:
...
print(f.read())
...
# with문을 빠져 나가거나, 예외가 발생하더라도 파일을 닫아준다.
Magic Methods for with statement
기본적으로 파이썬의 open
메쏘드는 with
구문과 함께 사용할 수 있습니다. 그 외에 사용이 끝난 리소스의 release를 보장하기 위해 with
구문을 사용하고 싶다면 어떻게 해야할까요?
파이썬 클래스의 Magic Methods를 이용하면 됩니다. Magic Methods란 클래스의 __init__
메소드와 같이 특정한 기능을 하도록 미리 설계된 메소드를 의미합니다. with
구문을 사용하기 위해서는 클래스에 __enter__
과 __exit__
이라는 Magic Methods를 정의해주면 됩니다.
__enter__(self)
:with
구문에 진입하는 시점에 자동으로 호출되는 메소드__exit__(self, type, value, traceback)
:with
구문을 빠져나오기 직전에 호출되는 메소드type
,value
,traceback
는 with문을 빠져나오기 전에 예외가 발생했을 때의 정보를 나타냄
아래의 예제처럼 사용할 수 있습니다.
아래의 예제는 Joel Ramos의 Python Context Managers and the “with” Statement에 나온 예제와 동일합니다.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
class SQLAlchemyDBConnection(object):
"""SQLAlchemy database connection"""
def __init__(self, connection_string):
self.connection_string = connection_string
self.session = None
# with구문 진입시에 db와 connection을 하고
# ORM을 사용하기 위한 session을 만들어준다.
def __enter__(self):
engine = create_engine(self.connection_string)
Session = sessionmaker()
self.session = Session(bind=engine)
return self
# with구문을 빠져나오기 전 session의 종료를 장한다.
def __exit__(self, exc_type, exc_val, exc_tb):
self.session.close()
위와 같이 SQLAlchemyDBConnection
클래스를 만들면 아래와 같이 with 구문과 함께 사용할 수 있습니다.
conn_str = 'mssql+pydobc://server_name/db_name?driver=SQL+Server'
db = SQLAlchemyDBConnection(conn_str)
with db:
customer = db.session.query(Customer).filter_by(id=123).one()
print(customer.name)
contextmanager decorator
클래스를 정의하는 것 외에도 contextmanager decorator를 사용하는 것도 방법입니다. 아래는 파이썬 공식문서에 있는 예제입니다.
from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource)
>>> with managed_resource(timeout=3600) as resource:
... # Resource is released at the end of this block,
... # even if code in the block raises an exception
Async Context Manager
위에서 설명한 Context Manager는 모두 동기적으로 실행됩니다. Python 3.7부터는 Context Manager를 비동기적으로 사용할 수 있습니다.
# from python 3.5
class AsyncContextManager:
async def __aenter__(self):
await log('entering context')
async def __aexit__(self, exc_type, exc, tb):
await log('exiting context')
# from python 3.7
from contextlib import asynccontextmanager
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
async def get_all_users():
async with get_connection() as conn:
return conn.query('SELECT ...')
참조 사이트
with 구문과 컨텍스트 매니저 - https://soooprmx.com/archives/4079
Python Tips - http://book.pythontips.com/en/latest/context_managers.html
Python Context Managers and the “with” Statement - https://medium.com/@ramojol/python-context-managers-and-the-with-statement-8f53d4d9f87
파이썬 공식문서 - https://docs.python.org/3/library/contextlib.html
'개발 > 파이썬' 카테고리의 다른 글
파이썬과 비동기 프로그래밍 #3, 파이썬에서 비동기 프로그래밍 활용하기 (14) | 2019.03.01 |
---|---|
파이썬과 비동기 프로그래밍 #2, 파이썬에서 비동기 프로그래밍 시작하기 (0) | 2019.03.01 |
파이썬과 비동기 프로그래밍 #1, 비동기 프로그래밍이란 (0) | 2019.03.01 |
파이썬 단위 테스트 모듈 unittest에 대해 알아보자 (0) | 2019.02.24 |
ANACONDA 자주쓰는 명령어 정리 (0) | 2019.02.17 |