2021. 10. 25. 15:40ㆍ[개발] 지식/Python
Python with절 문법의 이해
자원을 획득하고, 사용하고, 반납할때 주로 사용한다.
예를들어 파일을 여는 경우, 다른 프로세스를 위해 사용한 뒤에 닫아주어야 한다.
또는 DB 세션을 사용하는 경우, 다른 프로세스를 위해 반납해야 한다.
try
/ except
/ finally
구문을 통해 이와같은 구현이 가능하긴 하다.
하지만 이러한 방법 역시 예외가 발생하는 케이스 및 탈출 조건을 만족하는 케이스에 대해서 리소스를 정리하는 코드가 중복으로 작성된다.
파이썬의 컨텍스트 매니저는 이러한 리소스를 with
문법을 통해 with
절 내에서만 액세스를 가능하게 하고, 블록을 나가는 경우 어떤 이유든간에 리소스를 해제하게 된다.
다음과 같은 구조로 사용한다.
with {expression} as {variable}:
block..
샘플 코드로 보면..
아래와 같이 파일을 읽고, 종료하는 전통적인 코드를
f = open('myFile.txt', 'w', encoding='utf8')
f.write("test")
f.close()
아래처럼 좀 더 간결하고, 안전하게 사용할 수 있다.
with open('mytextfile.txt', 'r', encoding='utf8') as f:
f.wirte("test")
이와 같이 사용할 수 있는 이유는 파이썬의 파일핸들러가 내부적으로 ContextManager
프로토콜을 따르고 있기 때문이다. 다시말해, open()
을 통해 생성되는 파일 핸들이 아래와 같은 ContextManager
프로토콜을 따르고 있기 때문에 with
절에서 사용이 가능하다.
- 객체가 생성된 후,
with
블럭에 진입하면서 미리 지정된 특정한 작업을 수행한다. with
블럭을 떠나는 시점에 미리 지정된 특정한 작업을 수행한다.
또한, with
문의 장점은 중첩이 가능하다는 점이다. 어느 한 지점에서 문제가 발생하여 모든 블럭을 빠져나와야 하는 상황에서도 가장 하위의 블럭에서부터 파일을 순차적으로 닫고, 안전하게 리소르를 회수할 수 있다. 이는 로직의 전후관계를 추적할 필요가 없으므로 코드를 간결하게 만든다.
컨텍스트 매니저
컨텍스트 매니저는 with
구문에 쓰일 수 있는 객체의 타입이며, Context Manager
프로토콜을 준수한다. 컨텍스트 매니저는 다음 두 개의 메소드를 정의하고 있는 것으로 간주한다.
__enter__(self)
:with
문에 진입하는 시점에 자동으로 호출__exit__(self, type, value, traceback)
:with
문이 끝나기 직전에 자동으로 호출
__exit__()
메소드가 받는 세 개의 인자는 해당 객체와 연관된 컨텍스트 내에서 예외가 발생되었을때, 해당 예외에 관한 정보이다. 예외없이 with
구문이 종료되었다면 이 세 인자는 모두 None이 될 것이다.
DB 세션 예시
sqlalchemy를 통한 postgresql db 접속 클래스이다.
예시이므로 코드의 완성도는 생각하지 않았다.
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine, text
class SessionMaker:
def __enter__(self, engine):
# 초기화가 끝나고 with 블럭에 진입할 때 호출된다.
# 해당 함수는 하는일이 없더라도 필수로 구현되어야 한다.
if self.engine is not None:
self.session = sessionmaker(autocommit=True, autoflush=True, bind=engine)
else:
## 아직 컨텍스트 블럭에 진입 전이므로, 여기서 발생하는 예외는 밖으로 던져진다.
raise IOError("Cannot access DB")
def __exit__(self, e_type, e_value, tb):
## with 블럭이 끝나면 session을 닫는다.
print("Closing Database...")
self.session.close()
def getContacts(name):
query = """SELECT * FROM Contacts WHERE name = ? ;"""
self.cursor.execute(query, (name,))
return [dict(x) for x in self.cursor.fetchall()]
그리고 아래와 같이 사용한다.
engine = create_engine('postgresql://postgres:1234/localhost:9999/testDB')
with PostgresConnector(engine) as dbSession:
dbSession.execute('SELECT * from postgres')
engine
은 별도로 생성한다고 가정하고, 이를 파라미터로 넘기면..
with
절에 진입할 때 __enter__
에 진입해서 session
을 생성한다.
with
절 내부에서 쿼리를 실행하고, 빠져나올 때 자동으로 __exit__
함수가 실행되면서 session
을 종료한다.
sqlalchemy
에서는 아마 이러한 기능을 수행하는 sessionmaker
가 이미 구현되어 있을 것이므로, 가져다가 사용하면 될 것이다.
Ref.
'[개발] 지식 > Python' 카테고리의 다른 글
이미지를 Base64 인코딩 문자열로 변환해서 HTML에 삽입하기 (Python 사용) (0) | 2022.07.28 |
---|---|
R vs Python 모델 속도 비교 (0) | 2022.01.26 |
ubuntu에서 apt-get으로 python3.7, pip 설치 및 심볼릭 링크 설정하기 (0) | 2022.01.22 |
Linux(ubuntu)에서 Python 특정버전 설치하기 (1) | 2021.11.26 |
Python 싱글톤(Singleton) 패턴 적용 (0) | 2021.10.18 |