Python/문법

[Python] 데코레이터(Decorator) 1 - 데코레이터, 중첩 데코레이터

comgu 2024. 12. 11. 23:15
반응형

파이썬에서 데코레이터란 무엇인지, 그 동작에 대해 알아본다. 

먼저 그에 앞서 파이썬의 클로저(Closure)의 개념을 이해하면 더 수월하게 이해할 수 있다. 관련해서는 클로저를 다룬 글을 참고하면 된다

https://comgu.tistory.com/entry/Python-클로저Closure-함수

 

[Python] 클로저(Closure) 함수

파이선에서 클로저 함수란 무엇인지, 그 동작에 대해 알아본다. 또한 클로저와 데코레이터의 관계도 파악한다. 클로저 (Closure)클로저란, 함수 안에서 정의된 내부 함수가 외부 함수의 지역 변수

comgu.tistory.com

 

 

데코레이터(Decorator)

데코레이터는 함수나 메서드의 동작을 동적으로 확장하거나 수정할 수 있는 강력한 도구이다. 주로 코드 재사용성, 가독성, 유지보수를 개선하기 위해 사용된다.

데코레이터는 다른 함수를 인수로 받아 새로운 함수를 반환하는 함수이며, @ 기호를 사용해서 함수의 정의 위에 적용된다.

def decorator(func):
    def wrapper(*args, **kwargs):
        print("함수 호출 이전")
        result = func(*args, **kwargs)
        print("함수 호출 이후")
        return result
    return wrapper

@decorator
def hello():
    print("Hello, world!")

hello()
함수 호출 이전
Hello, world! 
함수 호출 이후

데코레이터를 사용하지 않은 경우, hello 함수의 호출은 다음과 같이 나타내야 한다. (즉 데코레이터를 쓴다는 것은 실제로는 아래의 방식대로 함수 호출이 된다는 의미이다.)

def hello():
    print("Hello, world!")
decorator(hello)()

decorator(hello)decorator 내부의 wrapper 함수를 의미한다. wrapper 함수는 상위의 decorator 함수의 scope에 있는 func 함수(즉 hello)를 참조하고 있는 상태이다. 그래서 wrapper 함수가 실행될 때, wrapper가 감싸고 있는 func(즉 hello) 함수가 호출될 수 있는 것이다.

 

데코레이터 내부의 wrapper 함수는 원 함수와 동일한 형식의 파라미터를 받아야 한다. 그렇지 않으면 데코레이터가 원래 함수에 제대로 적용되지 않을 수 있다.

보통 데코레이터의 wrapper 함수는 *args**kwargs를 사용하여 원 함수의 파라미터를 모두 받아 처리한다. 이렇게 하면 원 함수가 어떤 파라미터를 받든지 유연하게 대응할 수 있다.

def my_decorator(func):
    def wrapper(*args, **kwargs):  # 원 함수의 파라미터를 그대로 받음
        print("함수 호출 이전")
        result = func(*args, **kwargs)
        print("함수 호출 이후")
        return result

    return wrapper


@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")


say_hello("world")
함수 호출 이전
Hello, world!
함수 호출 이후

 

위 코드에서 wrapper 함수는 *args, **kwargs를 사용하여 say_hello 함수의 인자 name을 그대로 받을 수 있다.이 방식으로 데코레이터는 원 함수의 시그니처에 맞춰 유연하게 작동할 수 있다.

 

 

중첩(다중) 데코레이터

여러 데코레이터를 동시에 적용할 수도 있다.

아래는 3중 데코레이터를 적용한 예시이다.

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("Decorator 1")
        return func(*args, **kwargs)

    return wrapper


def decorator2(func):
    def wrapper(*args, **kwargs):
        print("Decorator 2")
        return func(*args, **kwargs)

    return wrapper


def decorator3(func):
    def wrapper(*args, **kwargs):
        print("Decorator 3")
        return func(*args, **kwargs)

    return wrapper


@decorator1
@decorator2
@decorator3
def my_function():
    print("Original function")


my_function()
Decorator 1
Decorator 2
Decorator 3
Original function

함수 정의에 가까운 데코레이터부터의 순서대로 적용된다. 첫번째 데코레이터는 원 함수의 호출을 감싸게 되고, 그 다음부터의 데코레이터는 이전 데코레이터에 의해 반환된 결과를 감싸게(wrap) 된다.

즉 데코레이터를 사용하지 않은 경우, my_function의 호출은 다음과 같이 나타내야 한다:

decorator1(decorator2(decorator3(my_function)))()

 

다음 글에서는 동적 데코레이터와 클래스 데코레이터에 대해 다뤄보고자 한다.

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

 

[Python] 데코레이터(Decorator) 2 - 동적 데코레이터

https://comgu.tistory.com/entry/Python-데코레이터Decorator-1-데코레이터-중첩-데코레이터위 글에 이어서 데코레이터에 대한 개념을 더 정리하고자, 동적 데코레이터에 대해 다뤄보겠다. 동적 데코레이터

comgu.tistory.com

반응형