수색…


소개

데코레이터 기능은 소프트웨어 디자인 패턴입니다. 서브 클래스를 직접 사용하거나 장식 된 함수의 소스 코드를 변경하지 않고 함수, 메소드 또는 클래스의 기능을 동적으로 변경합니다. 데코레이터를 올바르게 사용하면 개발 프로세스에서 강력한 도구가 될 수 있습니다. 이 주제는 파이썬에서 데코레이터 기능의 구현과 애플리케이션을 다룹니다.

통사론

  • def decorator_function (f) : pass #는 decorator_function이라는 데코레이터를 정의합니다.

  • @decorator_function
    def decorated_function () : pass # 함수가 이제 wrapped (데코 레이팅)되었습니다. decorator_function

  • decorated_function = decorator_function (decorated_function) # 이것은 구문 설탕 @decorator_function 을 사용하는 것과 같습니다.

매개 변수

매개 변수 세부
에프 데코 레이팅 (랩핑)되는 함수

데코레이터 기능

데코레이터는 다른 함수 나 메서드의 동작을 보강합니다. 함수를 매개 변수로 취하고 확장 된 함수를 리턴하는 함수는 데코레이터 로 사용할 수 있습니다.

# This simplest decorator does nothing to the function being decorated. Such
# minimal decorators can occasionally be used as a kind of code markers.
def super_secret_function(f):
    return f

@super_secret_function
def my_function():
    print("This is my secret function.")

@ 표기법은 다음에 해당 문법 설탕입니다 :

my_function = super_secret_function(my_function)

데코레이터의 작동 방식을 이해하려면 이것을 염두에 두는 것이 중요합니다. 이 "unsugared"구문은 데코레이터 함수가 인수로 함수를 사용하는 이유와 다른 함수를 반환해야하는 이유를 명확하게합니다. 또한 함수를 반환 하지 않으면 어떻게 될지를 보여줍니다.

def disabled(f):
    """
    This function returns nothing, and hence removes the decorated function
    from the local scope.
    """
    pass

@disabled
def my_function():
    print("This function can no longer be called...")

my_function()
# TypeError: 'NoneType' object is not callable

따라서 우리는 일반적으로 데코레이터 내부에 새 함수를 정의하고 반환합니다. 이 새로운 함수는 먼저 수행해야 할 작업을 수행 한 다음 원래 함수를 호출하고 마지막으로 반환 값을 처리합니다. 원래 함수가받는 인수를 출력하고 호출하는 간단한 장식 자 함수를 생각해보십시오.

#This is the decorator
def print_args(func):
    def inner_func(*args, **kwargs):
        print(args)
        print(kwargs)
        return func(*args, **kwargs) #Call the original function with its arguments.
    return inner_func

@print_args
def multiply(num_a, num_b):
    return num_a * num_b
  
print(multiply(3, 5))
#Output:
# (3,5) - This is actually the 'args' that the function receives.
# {} - This is the 'kwargs', empty because we didn't specify keyword arguments.
# 15 - The result of the function.

데코레이터 클래스

도입부에서 언급했듯이 데코레이터는 다른 기능에 적용하여 해당 기능을 보완 할 수있는 기능입니다. 구문 당은 다음과 같습니다. my_func = decorator(my_func) . 그러나 decorator 가 수업이 아닌 경우에는 어떻게해야합니까? my_funcdecorator 클래스의 인스턴스로 바뀌는 것을 제외하고는 구문이 여전히 작동합니다. 이 클래스가 __call__() 매직 메소드를 구현하면 my_func 를 함수처럼 사용할 수 있습니다.

class Decorator(object):
    """Simple decorator class."""

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('Before the function call.')
        res = self.func(*args, **kwargs)
        print('After the function call.')
        return res

@Decorator
def testfunc():
    print('Inside the function.')

testfunc()
# Before the function call.
# Inside the function.
# After the function call.

클래스 데코레이터로 장식 된 함수는 더 이상 유형 검사 관점에서 "함수"로 간주되지 않습니다.

import types
isinstance(testfunc, types.FunctionType)
# False
type(testfunc)
# <class '__main__.Decorator'>

데코레이션 메소드

데코 레이팅 메소드의 경우 추가 __get__ -method를 정의해야합니다.

from types import MethodType

class Decorator(object):
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs):
        print('Inside the decorator.')
        return self.func(*args, **kwargs)
    
    def __get__(self, instance, cls):
        # Return a Method if it is called on an instance
        return self if instance is None else MethodType(self, instance)

class Test(object):
    @Decorator
    def __init__(self):
        pass
    
a = Test()

실내 장식.

경고!

클래스 데코레이터는 특정 함수에 대해 하나의 인스턴스 만 생성하므로 클래스 데코레이터로 메소드를 꾸미면 해당 클래스의 모든 인스턴스간에 동일한 데코레이터가 공유됩니다.

from types import MethodType

class CountCallsDecorator(object):
    def __init__(self, func):
        self.func = func
        self.ncalls = 0    # Number of calls of this method
        
    def __call__(self, *args, **kwargs):
        self.ncalls += 1   # Increment the calls counter
        return self.func(*args, **kwargs)
    
    def __get__(self, instance, cls):
        return self if instance is None else MethodType(self, instance)

class Test(object):
    def __init__(self):
        pass
    
    @CountCallsDecorator
    def do_something(self):
        return 'something was done'
    
a = Test()
a.do_something()
a.do_something.ncalls   # 1
b = Test()
b.do_something()
b.do_something.ncalls   # 2

데코레이터를 데코 레이팅 된 함수처럼 보이게하기

데코레이터는 일반적으로 함수 메타 데이터를 동일하지 않으므로 제거합니다. 메타 프로그래밍을 사용하여 함수 메타 데이터에 동적으로 액세스 할 때 문제가 발생할 수 있습니다. 메타 데이터는 함수의 문서화 문자열과 이름도 포함합니다. functools.wraps 는 여러 속성을 래퍼 함수에 복사하여 장식 함수를 원래 함수처럼 보이게합니다.

from functools import wraps

데코레이터를 래핑하는 두 가지 방법은 원래 함수가 꾸며졌다는 사실을 숨기는 데있어 동일한 작업을 수행하는 것입니다. 이미 다른 버전을 사용하고있는 경우가 아니면 클래스 버전에 기능 버전을 선호 할 이유가 없습니다.

함수로서

def decorator(func):
    # Copies the docstring, name, annotations and module to the decorator
    @wraps(func)
    def wrapped_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapped_func

@decorator
def test():
    pass

test.__name__

'테스트'

수업으로

class Decorator(object):
    def __init__(self, func):
        # Copies name, module, annotations and docstring to the instance.
        self._wrapped = wraps(func)(self)
        
    def __call__(self, *args, **kwargs):
        return self._wrapped(*args, **kwargs)

@Decorator
def test():
    """Docstring of test."""
    pass

test.__doc__

'시험 닥스트.'

인수가있는 데코레이터 (데코레이터 팩토리)

데코레이터는 단 하나의 인수, 즉 장식 될 함수를 취합니다. 다른 주장을 전달할 방법이 없습니다.

그러나 추가적인 주장이 종종 필요합니다. 트릭은 임의의 인수를 취하여 장식자를 반환하는 함수를 만드는 것입니다.

데코레이터 기능

def decoratorfactory(message):
    def decorator(func):
        def wrapped_func(*args, **kwargs):
            print('The decorator wants to tell you: {}'.format(message))
            return func(*args, **kwargs)
        return wrapped_func
    return decorator

@decoratorfactory('Hello World')
def test():
    pass

test()

데코레이터가 당신에게 말하고 싶어합니다 : Hello World

중요 사항:

이러한 데코레이터 팩토리를 사용하면 괄호 쌍으로 데코레이터를 호출 해야합니다 .

@decoratorfactory # Without parentheses
def test():
    pass

test()

TypeError : decorator () missing 1 필수 위치 인수 : 'func'

데코레이터 클래스

def decoratorfactory(*decorator_args, **decorator_kwargs):
    
    class Decorator(object):
        def __init__(self, func):
            self.func = func

        def __call__(self, *args, **kwargs):
            print('Inside the decorator with arguments {}'.format(decorator_args))
            return self.func(*args, **kwargs)
        
    return Decorator

@decoratorfactory(10)
def test():
    pass

test()

인수가있는 데코레이터 내부 (10,)

데코레이터로 싱글 톤 클래스 만들기

싱글 톤은 클래스의 인스턴스화를 하나의 인스턴스 / 객체로 제한하는 패턴입니다. 데코레이터를 사용하여 클래스를 기존 클래스 인스턴스를 반환하거나 (존재하지 않는 경우) 새 인스턴스를 만들도록하여 클래스를 싱글 톤으로 정의 할 수 있습니다.

def singleton(cls):    
    instance = [None]
    def wrapper(*args, **kwargs):
        if instance[0] is None:
            instance[0] = cls(*args, **kwargs)
        return instance[0]

    return wrapper

이 데코레이터는 모든 클래스 선언에 추가 될 수 있으며 클래스의 인스턴스가 최대 하나만 생성되도록합니다. 후속 호출은 이미 존재하는 클래스 인스턴스를 반환합니다.

@singleton
class SomeSingletonClass:
    x = 2
    def __init__(self):
        print("Created!")

instance = SomeSingletonClass()  # prints: Created!
instance = SomeSingletonClass()  # doesn't print anything
print(instance.x)                # 2

instance.x = 3
print(SomeSingletonClass().x)    # 3

따라서 로컬 변수를 통해 클래스 인스턴스를 참조하는지 또는 다른 "인스턴스"를 작성하는지 여부는 중요하지 않습니다. 항상 동일한 객체를 가져옵니다.

데코레이터를 사용하여 함수 시간 측정하기

import time
def timer(func):
    def inner(*args, **kwargs):
        t1 = time.time()
        f = func(*args, **kwargs)
        t2 = time.time()
        print 'Runtime took {0} seconds'.format(t2-t1)
        return f
    return inner

@timer
def example_function():
    #do stuff


example_function()


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow