개발을 하다 보면, 특정 함수가 실행되기 전에 꼭 선행해서 실행되어야 하는 함수가 있는 경우가 있다.
예를 들면, B 메소드를 호출하기 전에 반드시 A 메소드를 실행해주어야 하는 상황이다.
(물론, 반대로 함수 실행 후 호출해야 하는 후행 함수가 있는 경우도 있다.)
이럴 때, 매번 A 메소드를 수동으로 호출하도록 강제하는 것은 코드의 중복을 초래하고 실수를 유발할 수 있다.
이런 경우 사용하는 것이 파이썬의 '데코레이터(Decorator)' 기능이다.
데코레이터를 사용하면 특정 함수의 실행을 가로채서, 공통된 사전 작업을 수행한 뒤 본래 함수를 실행할 수 있다.
이렇게 하면 B 메소드를 호출할 때마다 자동으로 A 메소드가 먼저 실행되는 구조를 만들 수 있어,
코드의 중복을 줄이고 가독성을 높일 수 있다.
간단한 예제 코드는 다음과 같다.
먼저, 선행해서 실행되어야 할 함수가 Toramko 라는 클래스 내에 선언되어 있다고 가정한다.
데코레이터 함수는 다음과 같이 선언할 수 있다. ('call_wrapper')
class Toramko:
@classmethod
def A(cls):
# 선행 함수 내용
def call_wrapper(before_call: Callable = None):
def outer_wrapper(func):
@wraps(func)
def inner_wrapper():
before_call()
return func()
return innter_wrapper
return outer_wrapper
데코레이터 함수에서 Callable 을 인자로 전달 받아('before_call')
func() 실행 전 먼저 before_call() 을 실행하도록 되어 있다.
이제 해당 데코레이터를 적용시키면 되는데, 여기서 주의할 점이 몇 가지 있다.
B 함수에 call_wrapper 데코레이터를 다음과 같이 적용해보려고 한다.
class Toramko:
@classmethod
def A(cls):
# 선행 함수 내용
@call_wrapper(A)
def B(cls):
# 함수 B 내용
1) 데코레이터에 전달하는 Callable 인자의 형태는 함수 자체여야 한다.
즉, @call_wrapper(A()) 가 아닌, @call_wrapper(A) 여야 한다는 뜻.
A() 를 전달하게 되면 A 함수가 즉시 실행되어 그 반환값이 데코레이터에 전달된다.
그러나 우리가 원하는 것은 A 함수의 반환값이 아닌, 함수 자체를 데코레이터에 전달하는 것이다.
(데코레이터 내부에서 이 전달된 함수를 호출해야 하기 때문)
따라서 A 자체를 함수 참조로 넘겨야 데코레이터 안에서 적절한 시점에 A를 실행할 수 있게 된다.
2) B 메소드는 클래스 외부에 정의해야 한다.
위와 같이 Toramko 클래스 안에 A 함수와 B 함수가 모두 정의되어 있는 경우, 다음과 같이 에러가 난다.
python 'classmethod' object is not callable
그 이유는 Toramko 클래스가 완전히 정의되기 전에 @call_wrapper(A) 가 평가되기 때문이다.
파이썬은 클래스 정의를 위에서 아래로 순차 진행하기 때문에,
@call_wrapper(A) 를 해석할 때, A는 이미 클래스 내부에 정의되어 있긴 하지만 아직 Toramko 클래스가 완전히 생성되지 않은 상태이다.
그래서 클래스 내부의 A를 외부로 전달하려 할 때 문제가 생긴다.
따라서, 이 문제를 해결하기 위해서는 다음과 같이 B 메소드를 클래스 외부에서 정의하고
데코레이터에 전달하고자 하는 함수를 class 단위부터 함께 명시(Toramko.A)하는 방식으로 작성해야 한다.
class Toramko:
@classmethod
def A(cls):
# 선행 함수 내용
@call_wrapper(Toramko.A)
def B():
# 함수 B 내용
이렇게 하면 특정 클래스 내부 함수를 선행하는 기능을 데코레이터로 손쉽게 구현할 수 있다 😊
':: python' 카테고리의 다른 글
python :: markdown to HTML 변환하기 (+테이블 테두리 설정) (0) | 2023.05.08 |
---|---|
python :: yaml 파일 읽기, yaml 데이터 json data로 변환하기 (0) | 2023.04.26 |
python :: 파이썬 딕셔너리 복사(copy) 후 수정사항 반영되지 않도록 하기(deepcopy) (1) | 2022.11.21 |
python :: 파이썬 1, 2, 3차원 배열(list) 선언 및 초기화(with java) (0) | 2022.08.23 |
python :: duckling, jpype 에러 해결 (TypeError: Parser must be a string or character stream, not java.lang.String) (0) | 2022.04.08 |