개발

[Python] Decorator

kwony 2024. 5. 17. 10:45

파이썬에선 함수위에 데코레이터를 둬서 함수의 실행전후에 특정한 작업을 주입할 수 있다. 아래 코드에선 foo 함수에 logging 이란 데코레이터를 추가했고 실행 전에 before 실행 후엔 after 를 출력하도록 했다. 

 

def logging(func):
    def wrapper(*args, **kwargs):
        print("before")
        ret = func(*args, **kwargs)
        print("after")
        return ret

    return wrapper

@logging
def foo():
    print("foo")

foo()

 

 

실행 결과 의도했던 대로 출력된다. 로깅이나 실행 전후에 처리하고 싶은 로직을 넣어야하는 경우 유용하게 사용할 수 있다.

 

before
foo
after

 

 

그런데 데코레이터를 사용할때 주의할 점이 있다. 아래 코드를 실행하면 어떤 결과가 나올까?

 

def logging(func):
    def logs(*args, **kwargs):
        return func(*args, **kwargs)

    return logs

@logging
def foo(x):
    return x + 100

print(foo.__name__)

 

애초 우리가 의도한건 foo 의 함수 객체의 이름이므로 foo 가 출력돼야 한다. 그런데 결과 값은 데코레이터의 wrapper 함수인 logs 가 출력된다. 데코레이터 wrapper 함수를 사용하면서 기존 foo 함수의 __name__ 과 __doc__ 같은 정보가 덮어썼기 때문이다.

 

logs

 

 

파이썬의 functools 라는 라이브러리에서는 데코레이터의 이런 문제점을 보완하고자 functions.wraps 라는 함수를 제공한다. 데코레이터를 쌩으로 쓰지 않고 functions.wraps 를 사용하면 기존 함수의 정보가 보존된다.

 

from functools import wraps

def logging_wrapped(func):
    @wraps(func)
    def logs(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return logs
    
@logging_wrapped
def foo_wrapped(x):
    return x + 100
    
print(foo_wrapped.__name__) # foo_wrapped