Параметризованные декораторы в Python - PullRequest
1 голос
/ 15 апреля 2020

Я экспериментировал с различными типами декораторов в Python, и мне трудно понять параметризованные декораторы. Декоратор принимает вызываемые и возвращает вызываемые (в моем случае это вызываемая функция) В следующем коде: -

def check_non_negative(index):
    def validator(f):
        def wrap(*args):
            if args[index]<0:
                raise ValueError(
                    'Argument {} must be non negative.'.format(index))
            return f(*args)
        return wrap
    return validator

@check_non_negative(1)
def create_list(value,size):
    return [value]*size


create_list('a',3)

Здесь я вижу, что check_non_negative не является декоратором в соответствии с определением, но ведет себя как единица (во время Run Validator является актуальным декоратором). check_non_negative принимает целое число, а не вызываемое, но ведет себя как декоратор. Может кто-нибудь объяснить, почему?

Ответы [ 2 ]

4 голосов
/ 15 апреля 2020

check_non_negative технически не является декоратором по вашему определению, его можно назвать «фабрикой декораторов». Он возвращает validator, который является декоратором.

В основном, когда у вас есть:

@<expression>
def ...

, тогда <expression> должен вычислить декоратор, то есть вызываемый который принимает один аргумент, который также может быть вызван.

1 голос
/ 15 апреля 2020

Когда вы передаете параметры в функцию декоратора (например, @check_non_negative(1)), функция вызывается с этими параметрами, а затем возвращает декоратор, который принимает и возвращает функцию (как если бы вы использовали @validator без параметры).

Это легче понять с помощью аннотаций типов IMO:

import functools
from typing import cast, Callable, List, TypeVar


_Elem = TypeVar('_Elem')
_Func = TypeVar('_Func', bound=Callable)


def check_non_negative(index: int) -> Callable[[_Func], _Func]:
    def validator(f: _Func) -> _Func:
        @functools.wraps(f)
        def wrap(*args, **kwargs):
            if args[index] < 0:
                raise ValueError(
                    'Argument {} must be non negative.'.format(index))
            return f(*args, **kwargs)
        return cast(_Func, wrap)
    return validator


@check_non_negative(1)
def create_list(value: _Elem, size: int) -> List[_Elem]:
    return [value]*size

Так что check_non_negative принимает аргумент int и возвращает функцию (validator), которая принимает указать c тип функции (_Func, которая здесь относится к типу декорированной функции, например create_list) и возвращает функцию того же типа.

...