:: python

python :: 파이썬 데코레이터로 함수 실행 전후 로직 적용하기 & python 'classmethod' object is not callable 에러 해결

토람이 2025. 6. 9. 15:24

개발을 하다 보면, 특정 함수가 실행되기 전에 꼭 선행해서 실행되어야 하는 함수가 있는 경우가 있다.

예를 들면, 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 내용

 

 

이렇게 하면 특정 클래스 내부 함수를 선행하는 기능을 데코레이터로 손쉽게 구현할 수 있다 😊

300x250