Python Language
장식 자
수색…
소개
데코레이터 기능은 소프트웨어 디자인 패턴입니다. 서브 클래스를 직접 사용하거나 장식 된 함수의 소스 코드를 변경하지 않고 함수, 메소드 또는 클래스의 기능을 동적으로 변경합니다. 데코레이터를 올바르게 사용하면 개발 프로세스에서 강력한 도구가 될 수 있습니다. 이 주제는 파이썬에서 데코레이터 기능의 구현과 애플리케이션을 다룹니다.
통사론
def decorator_function (f) : pass #는 decorator_function이라는 데코레이터를 정의합니다.
@decorator_function
def decorated_function () : pass # 함수가 이제 wrapped (데코 레이팅)되었습니다. decorator_functiondecorated_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_func
가 decorator
클래스의 인스턴스로 바뀌는 것을 제외하고는 구문이 여전히 작동합니다. 이 클래스가 __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()