@contextmanager@contextmanager는 "뒷바라지 코드"라고 생각하면 이해하기 쉽습니다.
yield를 기준으로 앞 부분은 사전 작업, 뒷 부분은 사후 작업을 처리합니다.
이로써, 리소스 관리를 더 간결하고 안전하게 처리할 수 있습니다.
yield 앞쪽: 사전 작업(리소스 설정, 파일 생성 등)yield 뒤쪽: 사후 작업(리소스 정리, 파일 닫기 등)try ~ except ~ finally 구조와 함께 사용되어 안정성을 높입니다.with 문과 함께 사용됩니다.from contextlib import contextmanager
import os
@contextmanager
def write_open(path):
# 사전 작업: 디렉토리가 없으면 생성
dir_name = os.path.dirname(path)
if not os.path.isdir(dir_name):
os.makedirs(dir_name, exist_ok=True)
# 사전 작업: 파일 열기
fw = open(path, "w")
print(1) # 디버깅용 출력
try:
yield fw # 메인 동작 부분
finally:
# 사후 작업: 파일 닫기
fw.close()
print(3) # 디버깅용 출력
with write_open("ns/log.txt") as f:
print(2) # 메인 동작 부분
f.write("Hello world")
print(4)
출력하면
1
2
3
4
해석: 폴더가 없으면 만드는것은 그냥 메인이랑 벗어나므로 뒤에 숨기고(contextmanager) 메인에 집중하도록 하는 것입니다.
@wraps는 함수의 동작은 그대로 유지하면서, 함수의 겉모습(메타데이터)을 바꾸는 데 사용됩니다.
주로 데코레이터를 작성할 때 사용되며, 원래 함수의 이름, 문서 문자열(docstring), 기타 속성을 유지합니다.
데코레이터를 사용하면 함수의 이름과 문서 문자열이 변경될 수 있습니다.
@wraps를 사용하면 원래 함수의 이름과 문서 문자열을 유지하여 코드의 가독성과 디버깅 편의성을 높입니다.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Wrapper starts")
result = func(*args, **kwargs)
print("Wrapper ends")
return result
return wrapper
@my_decorator
def say_hello():
"""This function says hello."""
print("Hello, world!")
say_hello()
print(say_hello.__name__) # 함수 이름 출력
print(say_hello.__doc__) # 함수 문서 문자열 출력
#출력
Wrapper starts
Hello, world!
Wrapper ends
say_hello
This function says hello.
@wraps를 사용하지 않으면 함수 이름과 문서 문자열이 데코레이터의 내부 함수로 덮어쓰기됩니다.@wraps를 사용하면 원래 함수의 메타데이터(이름, docstring 등)를 유지할 수 있습니다.