Механизм уравнивания функций в питоне - PullRequest
0 голосов
/ 21 мая 2018

Я узнал, что мы можем приравнять одну функцию к другой в Python следующим образом:

def func_1(x)
    print("func_1")
    print(x)

def func_2(x)
    print("func_2")
    print(x)

func_1 =func_2

Итак, здесь происходит то, что каждый вызов func_1 выполняет func_2.

Однако я прочитало декораторах и следующем простом коде, иллюстрирующем их:

def our_decorator(func):
    def function_wrapper(x):
        print("Before calling " + func.__name__)
        func(x)
        print("After calling " + func.__name__)
    return function_wrapper

def foo(x):
    print("Hi, foo has been called with " + str(x))

print("We call foo before decoration:")
foo("Hi")

print("We now decorate foo with f:")
foo = our_decorator(foo)

print("We call foo after decoration:")
foo(42)

Здесь, как мы можем видеть в следующей строке:

foo = our_decorator(foo)

происходит нечто подобное предыдущему уравнению функции.Я подумал, что именно так могут работать декораторы, т. Е. Заменять вызов декоратора вызовом декоратора.

Однако, под этим впечатлением, если я напишу код, подобный следующему:

def our_decorator():
    def function_wrapper(x):
        print("Before calling " )
        foo(x)
        print("After calling " )
    return function_wrapper

def foo(x):
    print("Hi, foo has been called with " + str(x))

print("We call foo before decoration:")
foo("Hi")

print("We now decorate foo with f:")
foo = our_decorator()

print("We call foo after decoration:")
foo(42)

Приведенное выше приведёт к бесконечной рекурсии, печатающей бесконечное число «Перед вызовом».

Итак, я могу заключить, что декоратор должен быть чем-то, что принимает функцию в качестве аргумента.

Таким образом, приравнивание функций различается в этих двух случаях, а именно приравнивается к функции, которая принимает в качестве аргумента другую функцию, иприравнивая две функции, которые не принимают другую функцию в качестве аргумента.

Как эти два могут отличаться во внутренней реализации?

Ответы [ 3 ]

0 голосов
/ 21 мая 2018

Я надеюсь, что на ваш вопрос можно ответить, объяснив несколько терминов.

  1. Вы используете термин "приравнивать" для того, что обычно называют "присваиванием",name = expr является оператором присваивания.Имя name дается (присваивается) объекту, который является результатом выражения expr.

  2. В Python функции не рассматриваются как специальные.Иногда это выражается предложением «функции являются объектами первого класса» , и в основном это означает, что функциональные объекты можно назначать переменным (именам), передавать в качестве аргументов и т. Д. Так же, как числа, строки и другие.объекты.

  3. (функция) декоратор обрабатывает другую функцию.Это функция, которая принимает декорируемую функцию в качестве аргумента и возвращает ее «декорированную» (то есть улучшенную или модифицированную) версию.Иногда он только регистрирует функцию, например, как обработчик или как часть API, и возвращает ее без изменений.Для него существует специальный синтаксис:

@decorator
def func(...):
    pass

, что эквивалентно:

func = decorator(func)

, а также:

@decorator(args)
def func(...):
    pass

, что эквивалентно:

_real_decorator = decorator(args)
func = _real_decorator(func)

Поскольку этот синтаксис @decorator очень прост в использовании и легко читается, вы обычно не пишете:

func = decorator(func)

Подводя итог:

func1 = some_func является простым присвоением, присваивая другое имя some_func.

func2 = create_function(), это будет называться фабрикой функций в некоторых языках.Вы написали, что в вашем вопросе.

func = decorate_function(func) это украшение func

Примечание: существуют декораторы классов, они очень похожи, но улучшают определения классов вместо функций.

0 голосов
/ 21 мая 2018

Декоратор выглядит следующим образом:

def decorator_with_args(*args, **kwargs):
    def wrapper(f: "the function being decorated"):
        def wrapped(*args, **kwargs):
            # inside here is the code that is actually executed
            # when calling the decorated function. This should
            # always include...
            f(*args, **kwargs)

            # and usually return its result
        return wrapped
    return wrapper

# or

def decorator_without_args(f: "the function being decorated"):
    def wrapped(*args, **kwargs):
        # as above
        return f(*args, **kwargs)
    return wrapped

и используется:

@decorator_with_args("some", "args")
def foo(x):
    print("foo:", x)  # or whatever

@decorator_without_args
def bar(x):
    print("bar:", x)

Это эквивалентно определению каждой функции без магии @decorator... и последующему применению декоратора

def baz(x):
    print("baz:", x)

baz = decorator_with_args("some", "arguments")(baz)
# or
baz = decorator_without_args(baz)

В вашем примере кода вы вызываете foo внутри декоратора, затем вы декорируете foo этим декоратором, так что вы в конечном итоге будете повторяться бесконечно.Каждый раз, когда вы вызываете foo, он запускает ваш код декоратора, который также вызывает foo.Каждый раз, когда ваш декоратор вызывает foo, он запускает ваш код декоратора, который также вызывает foo.Каждый раз, когда декоратор вашего декоратора вызывает foo, он запускает код декоратора, который также ... и т.д.

0 голосов
/ 21 мая 2018

То, что вы называете «уравнительными функциями», на самом деле является просто присваиванием переменной.Определение функции (с def) создает функцию и присваивает ее имени переменной.После того, как вы выполните func_1 = func_2, у вас будет 2 переменные, ссылающиеся на одну и ту же функцию.

То, что происходит в примерах вашего декоратора, является естественным следствием предыдущего абзаца.Оставьте комментарий, если вам нужны дополнительные разъяснения.

...