Python - Функция Decorator приводит к тому, что функция вызывается дважды - PullRequest
1 голос
/ 26 июня 2019

Я использую декоратор функций в некоторых модульных тестах, которые пишу для тестирования кода.Однако я выяснил, что этот декоратор вызывает функцию, которая вызывается дважды (и, следовательно, выводит ее вывод дважды).

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

#!/usr/bin/env python3

def decorate(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("#" * 40)
        print("Testing function {}".format(func.__name__))
        print("Arguments passed: {} ".format(args))
        print("Begin output of {}".format(func.__name__))
        print("#" * 40)
        try:
            func(*args, **kwargs)
        except Exception as e:
            print("Error occured: {}".format(e))

        print("#" * 40)
        print("End of output of {}".format(func.__name__))
        print("#" * 40)
        print("\n" * 5)
        return func(*args,**kwargs) #Error happens on this line here
    return inner

#Add decorator to function definition. 
@decorate
def asdf():
    print("THIS SHOULD PRINT ONCE")

#Call function

asdf()

Вывод (интервал в точности копируется):

########################################
Testing function asdf
Arguments passed: ()
Begin output of asdf
########################################
THIS SHOULD PRINT ONCE
########################################
End of output of asdf
########################################






THIS SHOULD PRINT ONCE

Мой желаемый вывод:

########################################
Testing function asdf
Arguments passed: ()
Begin output of asdf
########################################
THIS SHOULD PRINT ONCE
########################################
End of output of asdf
########################################

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

Ответы [ 2 ]

4 голосов
/ 26 июня 2019

Обратите внимание, что вы вызываете функцию дважды из декоратора:

  • Один раз в блоке try/except.
  • и один раз при возврате.оператор.

Вы хотели бы изменить первый вызов (тот, что находится в блоке try) на:

res = func(*args, **kwargs)

, а затем просто:

return res

РЕДАКТИРОВАТЬ : (согласно комментарию @ DanielRoseman)

Поскольку вы не делаете рейз или не возвращаетесь из блока except, вы должны либо назначить res также там (внутри except) или альтернативно предварительно определите его в начале декоратора на None (или любое другое значение, подходящее вашей программе).

3 голосов
/ 26 июня 2019

Вы делаете звонок дважды, действительно, здесь жирным шрифтом:

def decorate(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("#" * 40)
        print("Testing function {}".format(func.__name__))
        print("Arguments passed: {} ".format(args))
        print("Begin output of {}".format(func.__name__))
        print("#" * 40)
        try:
            <b>func(*args,**kwargs)</b>
        except Exception as e:
            print("Error occured: {}".format(e))

        print("#" * 40)
        print("End of output of {}".format(func.__name__))
        print("#" * 40)
        print("\n" * 5)
        return <b>func(*args,**kwargs)</b>
    return inner

Возможно, вы захотите пропустить один из двух и сохранить результат во временной переменной, например:

def decorate(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("#" * 40)
        print("Testing function {}".format(func.__name__))
        print("Arguments passed: {} ".format(args))
        print("Begin output of {}".format(func.__name__))
        print("#" * 40)
        try:
            <b>result =</b> func(*args,**kwargs)
        except Exception as e:
            print("Error occured: {}".format(e))
            <b>result = None</b>

        print("#" * 40)
        print("End of output of {}".format(func.__name__))
        print("#" * 40)
        print("\n" * 5)
        return <b>result</b>
    return inner
...