데코레이터 체이닝 및 매개변수화된 데코레이터

Beginner

This tutorial is from open-source community. Access the source code

소개

이 랩에서는 Python 의 데코레이터 (decorator) 에 대해 배우게 됩니다. 데코레이터는 함수와 메서드의 동작을 수정할 수 있는 강력한 기능입니다. 데코레이터는 로깅 (logging), 성능 측정, 접근 제어, 타입 검사 (type checking) 와 같은 작업에 일반적으로 사용됩니다.

여러 데코레이터를 연결하고, 매개변수를 받는 데코레이터를 만들고, 데코레이터를 사용할 때 함수 메타데이터를 보존하며, 다양한 유형의 클래스 메서드에 데코레이터를 적용하는 방법을 배우게 됩니다. 작업할 파일은 logcall.py, validate.py, 그리고 sample.py입니다.

데코레이터에서 함수 메타데이터 보존하기

Python 에서 데코레이터는 함수의 동작을 수정할 수 있는 강력한 도구입니다. 하지만 데코레이터를 사용하여 함수를 래핑 (wrapping) 하면 작은 문제가 발생합니다. 기본적으로 원래 함수의 메타데이터, 즉 이름, 문서 문자열 (docstring), 그리고 어노테이션 (annotations) 이 손실됩니다. 메타데이터는 코드의 구조를 검사하는 인트로스펙션 (introspection) 과 문서를 생성하는 데 중요합니다. 먼저 이 문제를 확인해 보겠습니다.

WebIDE 에서 터미널을 엽니다. 데코레이터를 사용할 때 어떤 일이 발생하는지 확인하기 위해 몇 가지 Python 명령을 실행합니다. 다음 명령은 데코레이터로 래핑된 간단한 함수 add를 생성한 다음 함수와 docstring 을 출력합니다.

cd ~/project
python3 -c "from logcall import logged; @logged
def add(x,y):
    'Adds two things'
    return x+y
    
print(add)
print(add.__doc__)"

이 명령을 실행하면 다음과 유사한 출력이 표시됩니다.

<function wrapper at 0x...>
None

함수 이름이 add 대신 wrapper로 표시되는 것을 확인할 수 있습니다. 그리고 docstring 은 'Adds two things'여야 하지만 None입니다. 이는 인트로스펙션 도구 또는 문서 생성기와 같이 이 메타데이터에 의존하는 도구를 사용할 때 큰 문제가 될 수 있습니다.

functools.wraps 로 문제 해결하기

Python 의 functools 모듈이 해결책을 제시합니다. 이 모듈은 함수 메타데이터를 보존하는 데 도움이 되는 wraps 데코레이터를 제공합니다. logged 데코레이터를 수정하여 wraps를 사용하는 방법을 살펴보겠습니다.

  1. 먼저 WebIDE 에서 logcall.py 파일을 엽니다. 터미널에서 다음 명령을 사용하여 프로젝트 디렉토리로 이동할 수 있습니다.
cd ~/project
  1. 이제 logcall.py에서 logged 데코레이터를 다음 코드로 업데이트합니다. 여기에서 @wraps(func) 데코레이터가 핵심입니다. 이 데코레이터는 원래 함수 func의 모든 메타데이터를 래퍼 함수로 복사합니다.
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
  1. @wraps(func) 데코레이터는 중요한 역할을 합니다. 원래 함수 func에서 모든 메타데이터 (이름, docstring, 어노테이션 등) 를 가져와 wrapper 함수에 연결합니다. 이렇게 하면 데코레이팅된 함수를 사용할 때 올바른 메타데이터를 갖게 됩니다.

  2. 개선된 데코레이터를 테스트해 보겠습니다. 터미널에서 다음 명령을 실행합니다.

python3 -c "from logcall import logged; @logged
def add(x,y):
    'Adds two things'
    return x+y
    
print(add)
print(add.__doc__)"

이제 다음을 볼 수 있습니다.

<function add at 0x...>
Adds two things

훌륭합니다! 함수 이름과 docstring 이 보존되었습니다. 즉, 데코레이터가 예상대로 작동하며 원래 함수의 메타데이터가 그대로 유지됩니다.

validate.py 데코레이터 수정하기

이제 validate.pyvalidated 데코레이터에 동일한 수정을 적용해 보겠습니다. 이 데코레이터는 함수의 어노테이션을 기반으로 함수 인수의 유형과 반환 값을 검증하는 데 사용됩니다.

  1. WebIDE 에서 validate.py를 엽니다.

  2. @wraps 데코레이터로 validated 데코레이터를 업데이트합니다. 다음 코드는 이를 수행하는 방법을 보여줍니다. @wraps(func) 데코레이터는 메타데이터를 보존하기 위해 validated 데코레이터 내의 wrapper 함수에 추가됩니다.

from functools import wraps

class Integer:
    @classmethod
    def __instancecheck__(cls, x):
        return isinstance(x, int)

def validated(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ## Get function annotations
        annotations = func.__annotations__
        ## Check arguments against annotations
        for arg_name, arg_value in zip(func.__code__.co_varnames, args):
            if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
                raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')

        ## Run the function and get the result
        result = func(*args, **kwargs)

        ## Check the return value
        if 'return' in annotations and not isinstance(result, annotations['return']):
            raise TypeError(f'Expected return value to be {annotations["return"].__name__}')

        return result
    return wrapper
  1. 이제 validated 데코레이터가 메타데이터를 보존하는지 테스트해 보겠습니다. 터미널에서 다음 명령을 실행합니다.
python3 -c "from validate import validated, Integer; @validated
def multiply(x: Integer, y: Integer) -> Integer:
    'Multiplies two integers'
    return x * y
    
print(multiply)
print(multiply.__doc__)"

다음과 같은 출력을 볼 수 있습니다.

<function multiply at 0......>
Multiplies two integers

이제 loggedvalidated 데코레이터 모두 데코레이팅하는 함수의 메타데이터를 제대로 보존합니다. 이렇게 하면 이러한 데코레이터를 사용할 때 함수가 원래 이름, docstring 및 어노테이션을 유지하므로 코드 가독성과 유지 관리성이 매우 향상됩니다.

인수를 사용하는 데코레이터 생성하기

지금까지는 항상 고정된 메시지를 출력하는 @logged 데코레이터를 사용했습니다. 하지만 메시지 형식을 사용자 정의하고 싶다면 어떻게 해야 할까요? 이 섹션에서는 인수를 허용하여 데코레이터를 사용하는 방식을 더욱 유연하게 만들어주는 새로운 데코레이터를 만드는 방법을 배우겠습니다.

매개변수화된 데코레이터 이해하기

매개변수화된 데코레이터는 특수한 유형의 함수입니다. 다른 함수를 직접 수정하는 대신 데코레이터를 반환합니다. 매개변수화된 데코레이터의 일반적인 구조는 다음과 같습니다.

def decorator_with_args(arg1, arg2, ...):
    def actual_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            ## Use arg1, arg2, ... here
            ## Call the original function
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

코드에서 @decorator_with_args(value1, value2)를 사용하면 Python 은 먼저 decorator_with_args(value1, value2)를 호출합니다. 이 호출은 실제 데코레이터를 반환하며, 이 데코레이터는 @ 구문 다음에 오는 함수에 적용됩니다. 이 두 단계 프로세스는 매개변수화된 데코레이터가 작동하는 방식의 핵심입니다.

logformat 데코레이터 생성하기

형식 문자열을 인수로 사용하는 @logformat(fmt) 데코레이터를 만들어 보겠습니다. 이렇게 하면 로깅 메시지를 사용자 정의할 수 있습니다.

  1. WebIDE 에서 logcall.py를 열고 새 데코레이터를 추가합니다. 아래 코드는 기존 logged 데코레이터와 새 logformat 데코레이터를 모두 정의하는 방법을 보여줍니다.
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

def logformat(fmt):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(fmt.format(func=func))
            return func(*args, **kwargs)
        return wrapper
    return decorator

logformat 데코레이터에서 외부 함수 logformat은 형식 문자열 fmt를 인수로 받습니다. 그런 다음 대상 함수를 수정하는 실제 데코레이터인 decorator 함수를 반환합니다.

  1. 이제 sample.py를 수정하여 새 데코레이터를 테스트해 보겠습니다. 다음 코드는 서로 다른 함수에서 loggedlogformat 데코레이터를 모두 사용하는 방법을 보여줍니다.
from logcall import logged, logformat

@logged
def add(x, y):
    "Adds two numbers"
    return x + y

@logged
def sub(x, y):
    "Subtracts y from x"
    return x - y

@logformat('{func.__code__.co_filename}:{func.__name__}')
def mul(x, y):
    "Multiplies two numbers"
    return x * y

여기서 addsub 함수는 logged 데코레이터를 사용하고, mul 함수는 사용자 정의 형식 문자열과 함께 logformat 데코레이터를 사용합니다.

  1. 업데이트된 sample.py를 실행하여 결과를 확인합니다. 터미널을 열고 다음 명령을 실행합니다.
cd ~/project
python3 -c "import sample; print(sample.add(2, 3)); print(sample.mul(2, 3))"

다음과 유사한 출력을 볼 수 있습니다.

Calling add
5
sample.py:mul
6

이 출력은 logged 데코레이터가 예상대로 함수 이름을 출력하고, logformat 데코레이터가 사용자 정의 형식 문자열을 사용하여 파일 이름과 함수 이름을 출력하는 것을 보여줍니다.

logformat 을 사용하여 logged 데코레이터 재정의하기

이제 더 유연한 logformat 데코레이터가 있으므로 이를 사용하여 원래 logged 데코레이터를 재정의할 수 있습니다. 이렇게 하면 코드를 재사용하고 일관된 로깅 형식을 유지하는 데 도움이 됩니다.

  1. 다음 코드로 logcall.py를 업데이트합니다.
from functools import wraps

def logformat(fmt):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(fmt.format(func=func))
            return func(*args, **kwargs)
        return wrapper
    return decorator

## Define logged using logformat
logged = lambda func: logformat("Calling {func.__name__}")(func)

여기서는 람다 함수를 사용하여 logformat 데코레이터를 기준으로 logged 데코레이터를 정의합니다. 람다 함수는 함수 func를 가져와 특정 형식 문자열과 함께 logformat 데코레이터를 적용합니다.

  1. 재정의된 logged 데코레이터가 여전히 작동하는지 테스트합니다. 터미널을 열고 다음 명령을 실행합니다.
cd ~/project
python3 -c "from logcall import logged; @logged
def greet(name):
    return f'Hello, {name}'
    
print(greet('World'))"

다음과 같은 출력을 볼 수 있습니다.

Calling greet
Hello, World

이는 재정의된 logged 데코레이터가 예상대로 작동하며, 일관된 로깅 형식을 달성하기 위해 logformat 데코레이터를 성공적으로 재사용했음을 보여줍니다.

클래스 메서드에 데코레이터 적용하기

이제 데코레이터가 클래스 메서드와 어떻게 상호 작용하는지 살펴보겠습니다. Python 에는 인스턴스 메서드, 클래스 메서드, 정적 메서드 및 속성과 같은 다양한 유형의 메서드가 있기 때문에 약간 까다로울 수 있습니다. 데코레이터는 다른 함수를 가져와 명시적으로 수정하지 않고 후자 함수의 동작을 확장하는 함수입니다. 클래스 메서드에 데코레이터를 적용할 때는 이러한 다양한 메서드 유형과 데코레이터가 어떻게 작동하는지 주의해야 합니다.

문제 이해하기

@logged 데코레이터를 다양한 유형의 메서드에 적용할 때 어떤 일이 발생하는지 살펴보겠습니다. @logged 데코레이터는 메서드 호출에 대한 정보를 로깅하는 데 사용될 가능성이 높습니다.

  1. WebIDE 에서 methods.py라는 새 파일을 만듭니다. 이 파일에는 @logged 데코레이터로 데코레이팅된 다양한 유형의 메서드가 있는 클래스가 포함됩니다.
from logcall import logged

class Spam:
    @logged
    def instance_method(self):
        print("Instance method called")
        return "instance result"

    @logged
    @classmethod
    def class_method(cls):
        print("Class method called")
        return "class result"

    @logged
    @staticmethod
    def static_method():
        print("Static method called")
        return "static result"

    @logged
    @property
    def property_method(self):
        print("Property method called")
        return "property result"

이 코드에는 네 가지 유형의 메서드가 있는 Spam 클래스가 있습니다. 각 메서드는 @logged 데코레이터로 데코레이팅되고, 일부는 @classmethod, @staticmethod, @property와 같은 다른 내장 데코레이터로도 데코레이팅됩니다.

  1. 작동 방식을 테스트해 보겠습니다. 터미널에서 Python 명령을 실행하여 이러한 메서드를 호출하고 출력을 확인합니다.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"

이 명령을 실행하면 몇 가지 문제가 발생할 수 있습니다.

  • @property 데코레이터가 @logged 데코레이터와 제대로 작동하지 않을 수 있습니다. @property 데코레이터는 메서드를 속성으로 정의하는 데 사용되며 특정 방식으로 작동합니다. @logged 데코레이터와 결합하면 충돌이 발생할 수 있습니다.
  • @classmethod@staticmethod의 경우 데코레이터의 순서가 중요합니다. 데코레이터가 적용되는 순서에 따라 메서드의 동작이 변경될 수 있습니다.

데코레이터의 순서

여러 데코레이터를 적용하면 아래에서 위로 적용됩니다. 즉, 메서드 정의에 가장 가까운 데코레이터가 먼저 적용된 다음 그 위의 데코레이터가 순서대로 적용됩니다. 예를 들어:

@decorator1
@decorator2
def func():
    pass

이는 다음과 같습니다.

func = decorator1(decorator2(func))

이 예에서 decorator2가 먼저 func에 적용된 다음 decorator1decorator2(func)의 결과에 적용됩니다.

데코레이터 순서 수정하기

methods.py 파일을 업데이트하여 데코레이터 순서를 수정해 보겠습니다. 데코레이터의 순서를 변경하면 각 메서드가 예상대로 작동하도록 할 수 있습니다.

from logcall import logged

class Spam:
    @logged
    def instance_method(self):
        print("Instance method called")
        return "instance result"

    @classmethod
    @logged
    def class_method(cls):
        print("Class method called")
        return "class result"

    @staticmethod
    @logged
    def static_method():
        print("Static method called")
        return "static result"

    @property
    @logged
    def property_method(self):
        print("Property method called")
        return "property result"

이 업데이트된 버전에서:

  • instance_method의 경우 순서는 중요하지 않습니다. 인스턴스 메서드는 클래스의 인스턴스에서 호출되며 @logged 데코레이터는 기본 기능에 영향을 주지 않고 어떤 순서로든 적용할 수 있습니다.
  • class_method의 경우 @logged 다음에 @classmethod를 적용합니다. @classmethod 데코레이터는 메서드가 호출되는 방식을 변경하며, @logged 다음에 적용하면 로깅이 올바르게 작동하도록 합니다.
  • static_method의 경우 @logged 다음에 @staticmethod를 적용합니다. @classmethod와 마찬가지로 @staticmethod 데코레이터는 자체 동작을 가지며 @logged 데코레이터와의 순서가 올바르게 지정되어야 합니다.
  • property_method의 경우 @logged 다음에 @property를 적용합니다. 이렇게 하면 로깅 기능도 얻으면서 속성 동작이 유지됩니다.
  1. 업데이트된 코드를 테스트해 보겠습니다. 이전과 동일한 명령을 실행하여 문제가 해결되었는지 확인합니다.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"

이제 모든 메서드 유형에 대해 적절한 로깅을 볼 수 있습니다.

Calling instance_method
Instance method called
instance result
Calling class_method
Class method called
class result
Calling static_method
Static method called
static result
Calling property_method
Property method called
property result

메서드 데코레이터에 대한 모범 사례

메서드 데코레이터로 작업할 때는 다음 모범 사례를 따르십시오.

  1. 메서드 변환 데코레이터 (@classmethod, @staticmethod, @property) 를 사용자 지정 데코레이터 다음에 적용합니다. 이렇게 하면 사용자 지정 데코레이터가 먼저 로깅 또는 기타 작업을 수행한 다음 내장 데코레이터가 의도한 대로 메서드를 변환할 수 있습니다.
  2. 데코레이터 실행은 메서드 호출 시점이 아닌 클래스 정의 시점에 발생한다는 점을 인지하십시오. 즉, 데코레이터의 모든 설정 또는 초기화 코드는 메서드가 호출될 때가 아니라 클래스가 정의될 때 실행됩니다.
  3. 더 복잡한 경우에는 서로 다른 메서드 유형에 대해 특수 데코레이터를 만들어야 할 수 있습니다. 서로 다른 메서드 유형은 서로 다른 동작을 가지며, 모든 경우에 적용되는 데코레이터는 모든 상황에서 작동하지 않을 수 있습니다.

인수를 사용하여 유형 강제 데코레이터 생성하기

이전 단계에서 @validated 데코레이터에 대해 배웠습니다. 이 데코레이터는 Python 함수에서 유형 주석을 적용하는 데 사용됩니다. 유형 주석은 함수 인수와 반환 값의 예상 유형을 지정하는 방법입니다. 이제 한 단계 더 나아가겠습니다. 유형 사양을 인수로 허용할 수 있는 더 유연한 데코레이터를 만들 것입니다. 즉, 각 인수와 반환 값에 대해 원하는 유형을 보다 명시적으로 정의할 수 있습니다.

목표 이해하기

우리의 목표는 @enforce() 데코레이터를 만드는 것입니다. 이 데코레이터를 사용하면 키워드 인수를 사용하여 유형 제약 조건을 지정할 수 있습니다. 작동 방식의 예는 다음과 같습니다.

@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
    return x + y

이 예에서는 @enforce 데코레이터를 사용하여 add 함수의 xy 인수가 Integer 유형이어야 하고 반환 값도 Integer 유형이어야 함을 지정합니다. 이 데코레이터는 이전 @validated 데코레이터와 유사하게 작동하지만 유형 사양을 더 많이 제어할 수 있습니다.

enforce 데코레이터 생성하기

  1. 먼저 WebIDE 에서 validate.py 파일을 엽니다. 이 파일에 새 데코레이터를 추가합니다. 추가할 코드는 다음과 같습니다.
from functools import wraps

class Integer:
    @classmethod
    def __instancecheck__(cls, x):
        return isinstance(x, int)

def validated(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ## Get function annotations
        annotations = func.__annotations__
        ## Check arguments against annotations
        for arg_name, arg_value in zip(func.__code__.co_varnames, args):
            if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
                raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')

        ## Run the function and get the result
        result = func(*args, **kwargs)

        ## Check the return value
        if 'return' in annotations and not isinstance(result, annotations['return']):
            raise TypeError(f'Expected return value to be {annotations["return"].__name__}')

        return result
    return wrapper

def enforce(**type_specs):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            ## Check argument types
            for arg_name, arg_value in zip(func.__code__.co_varnames, args):
                if arg_name in type_specs and not isinstance(arg_value, type_specs[arg_name]):
                    raise TypeError(f'Expected {arg_name} to be {type_specs[arg_name].__name__}')

            ## Run the function and get the result
            result = func(*args, **kwargs)

            ## Check the return value
            if 'return_' in type_specs and not isinstance(result, type_specs['return_']):
                raise TypeError(f'Expected return value to be {type_specs["return_"].__name__}')

            return result
        return wrapper
    return decorator

이 코드가 수행하는 작업을 분석해 보겠습니다. Integer 클래스는 사용자 지정 유형을 정의하는 데 사용됩니다. validated 데코레이터는 함수의 유형 주석을 기반으로 함수 인수와 반환 값의 유형을 확인합니다. enforce 데코레이터는 우리가 만들고 있는 새 데코레이터입니다. 각 인수와 반환 값에 대한 유형을 지정하는 키워드 인수를 사용합니다. enforce 데코레이터의 wrapper 함수 내부에서 인수와 반환 값의 유형이 지정된 유형과 일치하는지 확인합니다. 그렇지 않은 경우 TypeError를 발생시킵니다.

  1. 이제 새 @enforce 데코레이터를 테스트해 보겠습니다. 예상대로 작동하는지 확인하기 위해 몇 가지 테스트 케이스를 실행합니다. 테스트를 실행하는 코드는 다음과 같습니다.
cd ~/project
python3 -c "from validate import enforce, Integer

@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
    return x + y

## This should work
print(add(2, 3))

## This should raise a TypeError
try:
    print(add('2', 3))
except TypeError as e:
    print(f'Error: {e}')

## This should raise a TypeError
try:
    @enforce(x=Integer, y=Integer, return_=Integer)
    def bad_add(x, y):
        return str(x + y)
    print(bad_add(2, 3))
except TypeError as e:
    print(f'Error: {e}')"

이 테스트 코드에서는 먼저 @enforce 데코레이터가 있는 add 함수를 정의합니다. 그런 다음 오류 없이 작동해야 하는 유효한 인수로 add 함수를 호출합니다. 다음으로, TypeError를 발생시켜야 하는 잘못된 인수로 add 함수를 호출합니다. 마지막으로, 잘못된 유형의 값을 반환하는 bad_add 함수를 정의합니다. 이 함수도 TypeError를 발생시켜야 합니다.

이 테스트 코드를 실행하면 다음과 유사한 출력을 볼 수 있습니다.

5
Error: Expected x to be Integer
Error: Expected return value to be Integer

이 출력은 @enforce 데코레이터가 올바르게 작동하고 있음을 보여줍니다. 인수 또는 반환 값의 유형이 지정된 유형과 일치하지 않으면 TypeError를 발생시킵니다.

두 가지 접근 방식 비교

@validated@enforce 데코레이터는 모두 유형 제약 조건을 적용하는 동일한 목표를 달성하지만 다른 방식으로 수행합니다.

  1. @validated 데코레이터는 Python 의 내장 유형 주석을 사용합니다. 예는 다음과 같습니다.

    @validated
    def add(x: Integer, y: Integer) -> Integer:
        return x + y

    이 접근 방식을 사용하면 유형 주석을 사용하여 함수 정의에서 직접 유형을 지정합니다. 이것은 Python 의 내장 기능이며 통합 개발 환경 (IDE) 에서 더 나은 지원을 제공합니다. IDE 는 이러한 유형 주석을 사용하여 코드 완성, 유형 검사 및 기타 유용한 기능을 제공할 수 있습니다.

  2. 반면에 @enforce 데코레이터는 키워드 인수를 사용하여 유형을 지정합니다. 예는 다음과 같습니다.

    @enforce(x=Integer, y=Integer, return_=Integer)
    def add(x, y):
        return x + y

    이 접근 방식은 데코레이터에 유형 사양을 직접 인수로 전달하기 때문에 더 명시적입니다. 다른 주석 시스템에 의존하는 라이브러리로 작업할 때 유용할 수 있습니다.

각 접근 방식에는 고유한 장점이 있습니다. 유형 주석은 Python 의 기본 부분이며 더 나은 IDE 지원을 제공하는 반면, @enforce 접근 방식은 더 많은 유연성과 명시성을 제공합니다. 작업 중인 프로젝트에 따라 요구 사항에 가장 적합한 접근 방식을 선택할 수 있습니다.

요약

이 랩에서는 데코레이터를 효과적으로 생성하고 사용하는 방법을 배웠습니다. functools.wraps를 사용하여 함수 메타데이터를 보존하고, 매개변수를 허용하는 데코레이터를 생성하고, 여러 데코레이터를 처리하고, 적용 순서를 이해하는 방법을 배웠습니다. 또한 다양한 클래스 메서드에 데코레이터를 적용하고 인수를 사용하는 유형 강제 데코레이터를 만드는 방법을 배웠습니다.

이러한 데코레이터 패턴은 Flask, Django, pytest 와 같은 Python 프레임워크에서 일반적으로 사용됩니다. 데코레이터를 마스터하면 더 유지 관리 가능하고 재사용 가능한 코드를 작성할 수 있습니다. 학습을 더 진행하려면 컨텍스트 관리자, 클래스 기반 데코레이터, 캐싱에 데코레이터 사용, 데코레이터를 사용한 고급 유형 검사를 탐색할 수 있습니다.