반응형

https://comgu.tistory.com/entry/Python-데코레이터Decorator-1-데코레이터-중첩-데코레이터

https://comgu.tistory.com/entry/Python-데코레이터Decorator-2-동적-데코레이터

데코레이터에 대한 마지막 주제로, 클래스형 데코레이터에 대해 다뤄보겠다.

 

클래스형 데코레이터

클래스형 데코레이터는 데코레이터를 클래스 형태로 구현하는 방식이다.

함수형 데코레이터와 마찬가지로, 클래스형 데코레이터도 함수나 메서드에 특정 기능을 추가하거나 수정할 때 사용된다.

클래스형 데코레이터의 핵심은 클래스의 __call__ 메소드를 정의하여 인스턴스가 호출 가능하도록 만드는 것이다.

실제로 함수도 "function" 클래스의 인스턴스이고 __call__ 메소드를 갖기 때문에 함수명()의 형태로 호출이 가능한 것이다.

class MyDecorator:
    def __init__(self, func):
        self.func = func  # 데코레이트할 함수 저장

    def __call__(self, *args, **kwargs):
        print("함수 실행 이전")
        result = self.func(*args, **kwargs)
        print("함수 실행 이후")
        return result


@MyDecorator
def add(x, y):
    print(f"{x} + {y} = {x + y}")


add(3, 4)
함수 실행 이전
3 + 4 = 7     
함수 실행 이후

실행순서

  1. @MyDecorator가 데코레이트된 함수(즉 add)에 적용될 때, MyDecorator 클래스의 __init__ 메서드가 호출되어 add가 func으로 전달된다.
  2. 데코레이트된 함수(add)가 호출될 때, __call__ 메서드가 실행된다.
  3. __call__ 메서드 안에서 추가 작업을 수행한 후 원래의 함수(self.func)를 호출한다.

 

만약 add 함수가 데코레이터를 사용하지 않았다면, 클래스형  데코레이터를 사용하기 위해 아래와 같은 방식을 사용한 것과 동일하다.

def add(x, y):
    print(f"{x} + {y} = {x + y}")


MyDecorator(add)(3, 4)
함수 실행 이전
3 + 4 = 7     
함수 실행 이후

 

클래스형 데코레이터의 장점

  • 상태 유지: 클래스 내부에 상태를 저장할 수 있어, 함수 호출 횟수 등을 추적할 수 있다.
  • 구조화된 코드: 복잡한 데코레이터 로직을 객체 지향적으로 관리할 수 있다.

 

 

상태를 저장하는 클래스형 데코레이터

클래스형 데코레이터를 쓰면, 클래스 내부에 상태를 저장하여 관리할 수 있다.

class CallCounter:
    def __init__(self, func):
        self.func = func
        self.count = 0  # 호출 횟수 추적

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} 함수 실행 카운트: {self.count}")
        return self.func(*args, **kwargs)


@CallCounter
def greet(name):
    print(f"Hello, {name}!")


greet("AAA")
greet("BBB")
greet("CCC")
greet 함수 실행 카운트: 1
Hello, AAA!
greet 함수 실행 카운트: 2
Hello, BBB!
greet 함수 실행 카운트: 3
Hello, CCC!

 

만약 greet 함수가 데코레이터를 사용하지 않았다면, 클래스형 데코레이터를 사용하기 위해 아래와 같은 방식을 사용한 것과 동일하다.

def greet(name):
    print(f"Hello, {name}!")


CallCounter(greet)("AAA")
CallCounter(greet)("BBB")
CallCounter(greet)("CCC")
greet 함수 실행 카운트: 1
Hello, AAA!
greet 함수 실행 카운트: 1
Hello, BBB!
greet 함수 실행 카운트: 1
Hello, CCC!

 

 

 

(고급) 파라미터를 받는 클래스형 데코레이터

클래스형 데코레이터에 인자를 전달하려면, 데코레이터의 역할을 하는 클래스의 인스턴스가 내부의 wrapper 함수를 한번 더 리턴할 수 있도록 해주면 된다.

class Repeat:
    def __init__(self, times):
        self.times = times

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for _ in range(self.times):
                func(*args, **kwargs)

        return wrapper


@Repeat(3)
def hello():
    print("Hello!")


hello()
Hello!
Hello!
Hello!

 

만약 hello 함수가 데코레이터를 사용하지 않았다면, 클래스형 데코레이터를 사용하기 위해 아래와 같은 방식을 사용한 것과 동일하다.

def hello():
    print("Hello!")


Repeat(3)(hello)()
Hello!
Hello!
Hello!

 

 

이것으로  데코레이터 시리즈의 마지막 글을 마친다.

(나중에 더 다룰 주제가 있으면 4탄 이상으로 추가할 생각이다)

반응형

+ Recent posts