| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 프로그래머스
- 개발자북클럽
- 특정 문자열로 끝나는 가장 긴 문자열 찾기
- 풀스택gpt
- 열반스쿨기초반
- 세 개의 구분자
- Python
- 파이썬
- 코딩테스트
- 배열 만들기6
- 문자열이 몇 번 등장하는지 세기
- 0 떼기
- 월부닷컴
- 노마드코더
- 열기반
- 프로그래머스 입문
- 첫번째로 나오는 음수
- l로 만들기
- 월급쟁이부자들
- 부동산강의
- 노개북
- 특별한 이차원 배열 1
- 월부
- 클린코드
- 무작위로 k개의 수 뽑기
- 배열 만들기3
- 기초
- 주사위 게임1
- 프로그래머스 기초
- 프로그래머스 코딩테스트 기초
- Today
- Total
해쟈스토리
매직메소드(__enter__와 __exit__, 그리고 contextlib) 본문
매직 메소드는 파이썬에서 특별한 기능을 제공하는 메소드로, 특정한 이름을 가진 메소드들을 의미한다. 이 메소드들은 클래스 내부에 정의되어 있으며, 클래스의 인스턴스에 대해서 특별한 동작을 수행한다.
매직 메소드의 이름은 양쪽에 밑줄(__)이 붙어있는 형태로 되어 있습니다. 예를 들어, __init__, __str__, __add__ 등이 매직 메소드의 예시다.
- __init__ 메소드는 객체의 초기화를 수행하는 메소드로, 객체가 생성될 때 자동으로 호출된다.
- __str__ 메소드는 객체를 문자열로 표현하는 메소드로, print() 함수 등에서 객체의 문자열 표현식으로 사용된다.
- __add__ 메소드는 두 개의 객체를 더하는 연산을 수행하는 메소드로, + 연산자를 사용할 때 호출된다.
매직 메소드를 정의하고 활용함으로써 객체의 동작을 커스터마이징할 수 있다.
__enter__ 와 __exit__
__enter__ 메소드는 컨텍스트 관리자(context manager)를 구현하기 위해 사용되는 매직 메소드다. 컨텍스트 관리자는 with 문과 함께 사용되며, 코드 블록을 실행하기 전에 설정을 초기화하고, 코드 블록 실행 후에 정리 작업을 수행하는 데 사용된다.
__exit__ 메소드는 컨텍스트 관리자를 벗어날 때 호출되는 매직 메소드다. with 문에서 코드 블록이 실행을 마치고 컨텍스트를 빠져나올 때, __exit__ 메소드가 자동으로 호출되어 리소스의 정리 작업을 수행한다.
그러면 컨텍스트 관리자란 무엇인가?
컨텍스트 관리자(Context Manager)는 리소스의 할당과 해제를 자동으로 처리하기 위해 사용되는 파이썬의 기능이다. 주로 with 문과 함께 사용되며, 특정한 코드 블록을 실행하기 전에 리소스를 설정하고, 코드 블록 실행 후에 리소스를 정리하는 작업을 수행한다.
컨텍스트 관리자를 사용하면 예외가 발생하더라도 리소스가 올바르게 해제되고, 코드 블록을 벗어날 때 자동으로 정리 작업이 수행됩니다. 이를 통해 리소스 누수를 방지하고, 코드를 더욱 안전하고 간결하게 작성할 수 있다.
컨텍스트 관리자는 파일, 네트워크 연결, 데이터베이스 연결 등과 같은 리소스 관리에 자주 사용된다. with 문을 사용하여 컨텍스트 관리자를 호출하면, __enter__ 메소드에서 리소스를 설정하고, 코드 블록이 실행되고, __exit__ 메소드에서 리소스를 정리하는 일련의 작업이 자동으로 처리된다.
with open('test.txt') as f:
contents = f.read()
print(contents)
위와 같이 파일 작업시에 with를 사용해서 작업하기도 하는데, as 키워드를 통해 __enter__ 메소드에서 반환한 값을 변수 f에 할당할 수 있다. __enter__에서 반환하는 값을 사용하지 않아도 되지만 반환하는 것은 좋은 습관이다.
with 문의 사용 목적을 명확하게 전달할 수 있기 때문이다. 또한, __enter__에서 반환하는 값을 활용하여 컨텍스트 관리자 내부에서 사용할 수 있다. 따라서, __enter__에서 값을 반환하는 것은 코드를 더 유연하고 확장 가능하게 만들어준다. 위에서도 f.read()를 사용하기 위해 반환한 것을 알 수 있다.
만약 DB를 백업하는 코드를 작성한다고 해보면, 데이버베이스 서비스를 중지하고 백업하고 다시 데이터베이스 서비스를 실행해야한다. 이때 항상 앞 뒤에 실행해야할 로직이 있으니 컨텍스트 관리자로 관리하면 좋다.
def stop_db():
run("systemctl stop postgresql.service")
def start_db():
run("systemctl start postgresql.service")
class DBHandler:
def __enter__(self):
stop_db()
return self
def __exit__(self, exc_type, ex_value, ex_traceback):
start_db()
def db_backup():
run("pg_dump database")
def main():
with DBHandler():
db_backup()
위와 같이 __enter__에서 self를 리턴하지만 특별히 사용하지는 않는다(as 변수할당 X) 그래도 반환하는 것은 좋은 습관이다. 정말로 필요없다면 None이라도 반환하는 것이 좋다.
__exit__에서 DBHandler에서 발생한 예외를 파라라미터로 받는다. 예외가 없으면 모두 None이다. 특별한 작업이 없다면 return이 없어도 된다. 그러나 True를 리턴하게 되면 예외가 소멸되어서 오류를 무시하게 된다. False를 리턴하게 되면 with문 밖에서 예외를 처리할 수 있도록 한다.
class MyContextManager:
def __enter__(self):
# 리소스 설정 작업
pass
def __exit__(self, exc_type, exc_value, traceback):
# 예외 처리 로직
return False
# 사용 예시
try:
with MyContextManager() as cm:
# 코드 블록
pass
except Exception as e:
# 예외 처리 로직
print("예외가 발생했습니다:", e)
__exit__메소드에서 return true를 하는 경우가 있는데 이 경우에는 __exit__ 메소드 안에서 이미 예외를 처리했을 경우이다.
class MyContextManager:
def __enter__(self):
# 리소스 설정 작업
pass
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None:
# 예외 처리 로직
print("예외가 발생했습니다:", exc_value)
return True
# 사용 예시
with MyContextManager() as cm:
# 코드 블록
pass
위의 예시에서 __exit__ 메소드에서는 exc_type이 None이 아닌 경우, 즉 예외가 발생한 경우에만 예외 처리 로직을 실행한다. 그리고 return True를 하여 예외를 소멸시킨다. 이렇게 하면 예외가 발생한 경우에도 with 문을 정상적으로 종료시키는 패턴이다.
그러니까, __exit__ 메소드에서 예외 처리와 관련된 로직을 구현하고, return True를 하는 경우는 예외를 처리하고 예외를 소멸시키는 걸 의미한다.
__exit__메소드에서 예외 처리를 하지 않았는데 True를 리턴하는 것은 예외를 무시하는 패턴이므로 좋지 않다.
contextlib 모듈로 컨텍스트 관리자 구현
contextlib 모듈은 컨텍스트 관리자 구현시 도움을 주는 모듈이다.함수에 contextlib.contextmanager 데코레이터를 적용하면 해당 함수의 코드를 컨텍스트 관리자로 변환한다. 함수는 제너레이터라는 특수한 형태여야 한다.
import contextlib
@contextlib.contextmanager
def db_handler():
try:
stop_database()
yield
finally:
start_database()
with db_handler():
db_backup()
데코레이터 contextlib.contextmanager를 적용하면 yield 문 앞의 모든 것은 __enter__ 메서드의 일부처럼 취급된다. __enter__ 메서드의 반환값과 같은 역할을 하는 것으로 as x:와 같은 형태로 변수에 할당 할 수 있다.
yield문 뒤에 오는 것들은 __exit__ 로직으로 볼 수 있다.
이렇게 컨텍스트 매니저를 작성하는 것은 리펙토링 측면에서 좋다. 일반적으로 어느 특정 객체에도 속하지 않는 컨텍스트 관리자가 필요한 경우 좋은 방법이다.(그렇지 않으면 객체 지향적인 의미에서 보면 아무런 목적을 가지지 않은 "가짜" 부모 클래스를 생성해야 한다 - 위에서 작성한 MyContextManager와 같은)
많은 상태를 관리할 필요가 없고 다른 클래스와 독립되어 있는 경우라면 컨텍스트 관리자를 만드는 것이 좋은 방법이다.
db_handler() 함수와 같이 이와 같이 함수 기반의 컨텍스트 관리자를 사용하면, 많은 상태를 관리할 필요가 없고 클래스와 독립적인 컨텍스트 관리자를 간단하게 구현할 수 있다. 이는 코드의 간결성과 유연성을 높여준다.
또 다른 도우미 클래스가 있다. contextlib.ContextDecorator이다. 컨텍스트 관리자 안에서 실행될 함수에 데코레이터를 적용하기 위한 로직을 제공하는 믹스인 클래스이다. 그러나 컨텍스트 관리자 자체의 로직은 앞서 언급한 매직 메서드를 구현하여 제공해야한다. ContextDecorator를 상속하여 새로운 클래스를 만들고 매직 메서드에 필요한 로직을 구현하면 된다.
class dbhandler_decorator(contextlib.ContextDecorator):
def __enter__(self):
stop_database()
return self
def __exit__(self, ext_type, ex_value, ex_traceback):
start_database()
@dbhandler_decorator()
def offline_backup():
run("pg_dump database")
@contextlib.contextmanager랑 contextlib.ContextDecoretor 클래스를 상속해서 하는 방법의 차이점은 뭘까?
바로 with문의 존재여부다. ContextDecorator 클래스를 상속하는 유일한 단점은 완전히 독립적이라는 점이다. 데코레이터는 함수에 대해 모르고 그 반대도 마찬가지이다. offline_backup에서 꼭 필요한 경우에도 데코레이터 객체에 직접 접근할 수 없다. 꼭 필요한 경우라면 다음과 같이 할 수는 있다.
def offline_backup():
with dbhandler_decorator() as handler: ...
@contextlib.contextmanager 데코레이터: 컨텍스트 관리자의 동작을 정의하는 함수를 작성할 때 사용한다. 한정적인 작업만 수행하고 상태를 유지할 필요가 없는 간단한 컨텍스트 관리자에 적합하다.
contextlib.ContextDecorator클래스: 기존의 함수나 클래스를 데코레이팅하여 컨텍스트 관리자로 사용할 때 사용한다. 기존 코드에 컨텍스트 관리 기능을 추가하고자 할 때 유용하다.
따라서, @contextlib.contextmanager 데코레이터는 함수 기반의 간단한 컨텍스트 관리자를 생성하는 데 적합하며, contextlib.ContextDecorator 클래스는 기존 코드에 컨텍스트 관리 기능을 추가하는 데 적합하다.선택은 개발자의 선호도와 상황에 따라 달라질 수 있다.
ㅌ
contextlib.suppress 함수는 with문과 함께 사용하여 특정 예외를 무시하고 특정행동을 할 수 있도록 기능을 제공하는 도우미 함수이다. 예외를 발생하더라고 해당 예외를 무시하고 계속 실행하고자 할 때 유용하다.
contextlib.suppress 함수는 인자로 받은 예외 클래스들을 처리하는 컨텍스트 관리자를 생성한다. 이 컨텍스트 관리자를 with 문과 함께 사용하면, 해당 예외가 발생할 때 예외를 무시하고 계속 실행한다.
import contextlib
with contextlib.suppress(FileNotFoundError):
# 파일을 찾는 코드
# FileNotFoundError 예외가 발생하더라도 무시하고 다음 코드를 실행함
pass
위의 예시에서 with contextlib.suppress(FileNotFoundError): 라인에서 FileNotFoundError 예외를 무시하는 컨텍스트 관리자를 생성한다. 이후의 코드 블록은 FileNotFoundError 예외가 발생하더라도 무시되고 다음 특정 행동이 실행된다.
contextlib.suppress 함수는 여러 예외 클래스를 인자로 받을 수 있으며, 콤마로 구분하여 지정할 수도 있다. 이를 통해 여러 예외를 동시에 처리 할 수 있다.
따라서, contextlib.suppress 함수는 특정 예외가 발생했을 때 특정 로직을 실행하고자 할 때 사용되는 도우미 함수다.
출처 : 유지보수가 쉬운 파이썬 코드를 만드는 비결, 파이썬 클린코드 2nd Edition - 마리아노 아나야