티스토리 뷰

with 절 왜필요한가

python을 사용하다보면 with 절 내에서 처리하는 경우가 많다.

특히 file 제어나 db 제어 할 때 많이 쓰이는데, 다음 파일을 열고 읽는 예제를 확인해보자.

with open("log.txt") as f:
	print(f.readline)

이 코드는 다음과 같은 기능을 하는 코드이다.

f = None
try:
	f = open("log.txt")
	print(f.readline)
finally:
	if f is Not None:
		f.close()

file을 열고 닫지 않은 경우 memory 누수가 발생하고 이러한 경우를 방지하기 위해 편리하게 나온 문법이 with 절이다.

with 절은 아무 객체에나 쓸 수 있나?

with 절에 대해서 잘 모르는 경우 직면하는 최초 궁금증이라고 생각된다.

with 절은 아무 객체에나 쓸 수 있을까?

당연히 아니다. 특정 함수를 구현한 객체에만 사용가능하다.

바로 __**entry**__ __**exit**__ 이다.

class MyContext:
    def __enter__(self):
        print("enter")
        return "value"
    # 인자
    # # exc_type: 예외타입
    # # exc_value: 예외값
    # # traceback: 트레이스 정보
    def __exit__(self, exc_type, exc_value, traceback):
        print("exit")

with MyContext() as ctx:
    print(ctx)
    
# 출력값
enter
value
exit

함수 정의부(시그니처)는 위와 같이 동일하게 작성한다. (duck typing 룰을 따르지만 pylance 에러가난다😢)

이러한 함수를 구현한 객체를 context manager로 칭한다.

🎯 @contextmanager 활용

python에서는 context manager를 쉽게 구현하기위해서 데코레이터를 제공하는데,

바로 @contextmanager 이다.

from contextlib import contextmanager

@contextmanager
def my_context():
    print("enter")
    yield "value"
    print("exit")

yield 구문과 결합해서 사용하면 정확히 같은 동작을 수행한다.

🎯 자주 사용하는 Context Manager

파일 처리

with open("file.txt", "r") as f:
    data = f.read()

DB Connection / Transaction 관리

with db.connect() as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT * FROM table")

Lock / Thread 동기화

from threading import Lock

lock = Lock()

with lock:
    critical_section()

timer 구현

import time
from contextlib import contextmanager

@contextmanager
def timer():
    start = time.perf_counter()
    yield
    end = time.perf_counter()
    print(f"Elapsed: {end - start:.4f}s")

# 사용
with timer():
    heavy_function()

이상으로 with 절에 대해서 깊숙히 알아보았다.

앞으로는 with 절을 만나면 반갑게 인사하자 😄

 

댓글