python

functools.wraps에 대해

시간을 거스르는자 2015. 4. 15. 16:52

functools.wraps는 보통 python fucntion을 wrapping할 때 원래 function의 정보들을 유지 시키기 위해서 사용된다.

간단한 wrapper 코드를 보자.


import functools


def print_iam_wrapper(method):

    @functools.wraps(method)        ----- 1

    def wrapper(*args, **kwargs):    ----- 2

        print "I'm wrapper"

        return method(*args, **kwargs)


    return wrapper


@print_iam_wrapper

def iam_func():

    print 'iam_func'


print iam_func.__name__                  ----- 3 

iam_func()                                       ----- 4


여기서 1번을 제거해도 4번의 결과는 같다. 하지만 3번의 결과는 다르다.

wraps가 하는 일은 wraps, update_wrapper, partial 코드를 살펴보면 이해가 간다.

(3개의 function에 대한 코드는 아래에 두겠다.)

1번 decorator가 실행될때 method가 넘어가서 넘어오는 것은 결국 wraps의 결과인 partial() 리턴값인데 이것은 newfunc 이다. 그렇다면 1번은 @newfunc가 되었다고 볼 수 있다.


그리고 @newfunc에 파라미터로 넘어가는 것은 아래있는 wrapper function이 된다. 이때 newfunc은 update_wrapper를 실행하고 있으므로 이는 또한 @update_wrapper와 같다. 다만 이때 wrapped로 처음에 전달된 method 즉 iam_func이 넘어가 있을 것이다. 결국 의미는 wrapped에 설정되어있는 '__module__', '__name__', '__doc__', '__dict__' attritbute를 wrapper에 셋팅하여 던져주는 것이다.



* 참고) update_wrapper, wraps, partial

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')

WRAPPER_UPDATES = ('__dict__',)

def update_wrapper(wrapper,

                   wrapped,

                   assigned = WRAPPER_ASSIGNMENTS,

                   updated = WRAPPER_UPDATES):

    for attr in assigned:

        setattr(wrapper, attr, getattr(wrapped, attr))

    for attr in updated:

        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

    # Return the wrapper so this can be used as a decorator via partial()

    return wrapper


def wraps(wrapped,

          assigned = WRAPPER_ASSIGNMENTS,

          updated = WRAPPER_UPDATES):


    return partial(update_wrapper, wrapped=wrapped,

                   assigned=assigned, updated=updated) 


def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc