앗! 광고가 차단되었어요!

글 내용이 방문자께 도움이 되었다면, 광고 차단 프로그램 해제를 고려해주세요 😀.

공돌이

Python: Decorator

this-gpa 2020. 10. 26. 16:06

 

오늘은 데코레이터의 예를 살펴보기로 했습니다.

먼저 데코레이터에 대해서 살펴볼까요?

Decorator with @ Syntax

The current syntax for function decorators as implemented in Python 2.4a2 is:

@dec2
@dec1
def func(arg1, arg2, ...):
    pass

This is equivalent to:

def func(arg1, arg2, ...):
    pass
func = dec2(dec1(func))

without the intermediate assignment to the variable func. The decorators are near the function declaration. The @ sign makes it clear that something new is going on here.

데코레이터는 객체를 감싸는 객체를 의미합니다. 감쌀 대상이 함수라면 함수 데코레이터, 클래스라면 클래스 데코레이터라고 합니다.

파이썬에서는 @ syntax를 이용하면, 자동으로 감싸진 객체를 반환하게 됩니다.
위 예에서 볼 수 있듯이, funcdec2(dec1(func))로 정의되며, func(...)dec2(dec1(func))(...)의 결과를 반환할 것입니다.

또 인자를 받아 데코레이터를 반환하는 함수를 사용할 수도 있습니다.

The current syntax also allows decorator declarations to call a function that returns a decorator:

@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
    pass

This is equivalent to:

func = decomaker(argA, argB, ...)(func)

First Example: @onexit

def onexit(f):
    import atexit
    atexit.register(f)
    return f

@onexit
def func():
    ...

func = onexit(func)이므로 onexit으로 데코레이팅 된 함수는 애플리케이션이 종료될 때 호출될 것입니다.

Note that this example is probably not suitable for real usage, but is for example purposes only.

다만 이런 예는 실제 적용하기는 어려울 수도 있다고 하네요.

Second Example: @singleton

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

MyClass = singleton(MyClass) 이므로 MyClass()clsMyClassgetinstnace() 가 됩니다. MyClass()는 언제나 instances에 저장된 객체를 반환할 것입니다.

Third Example: @attrs

def attrs(**kwds):
    def decorate(f):
        for k in kwds:
            setattr(f, k, kwds[k])
        return f
    return decorate

@attrs(versionadded="2.2",
       author="Guido van Rossum")
def mymethod(f):
    ...

함수에 속성을 추가하는 경우입니다. 위에서 추가하는 속성 versionadded, author 등은 문서화할 때 사용될 것 같네요. 함수 내부에서 설정할 필요 없이 데코레이터로 일괄 적용할 수 있겠습니다.

Fourth Example: @accepts, @returns

네 번째 예시는 데코레이팅 된 함수의 인자와 반환 값을 검사하는 데코레이터입니다.

def accepts(*types):
    def check_accepts(f):
        assert len(types) == f.func_code.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                   "return value %r does not match %s" % (result,rtype)
            return result
        new_f.func_name = f.func_name
        return new_f
    return check_returns

@accepts(int, (int,float))
@returns((int,float))
def func(arg1, arg2):
    return arg1 * arg2

func는 아래와 같이 됩니다.

func = accepts(int, (int, float))(
    returns((int, float))(func))

func 호출 시 주어지는 인자는 accepts에 의해 검사되고, 반환되는 값은 returns에 의해 검사됩니다. assert을 사용했기 때문에 타입이 맞지 않으면 에러가 발생합니다.

Fifth Example: @provides

다섯번째 예시는 인터페이스와 관련된 것으로, PyProtocols와 연관이 있다고 합니다. (PEAK, Pyprotocols 이런 용어가 나오는 데 저는 처음 들어보네요)

def provides(*interfaces):
     """
     An actual, working, implementation of provides for
     the current implementation of PyProtocols.  Not
     particularly important for the PEP text.
     """
     def provides(typ):
         declareImplementation(typ, instancesProvide=interfaces)
         return typ
     return provides

class IBar(Interface):
     """Declare something about IBar here"""

@provides(IBar)
class Foo(object):
        """Implement something here..."""

대부분 아시겠지만 파이썬에는 인터페이스 키워드가 없습니다. 작성하면서 알았는데 인터페이스 기능을 추가하는 PEP 245는 있지만 Rejected 상태입니다.

이번 예시는 이를 보완하는 데코레이터를 정의하는 것으로 보입니다.
상세한 코드 구현은 아니지만, 클래스가 *interfaces들을 구현/implements 한다의 느낌으로 이해했습니다.

References

PEP 318