Ошибка декоратора: объект NoneType не вызывается

Я написал функцию декоратора, как это:

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc()

@tsfunc
def foo():
    pass

foo()  # to get it work, use foo instead of foo()
foo()

Я получил следующее сообщение об ошибке:

foo() called
Traceback (most recent call last):
  File "decorator.py", line 11, in <module>
    foo()
TypeError: 'NoneType' object is not callable

Я получаю это, заменив "foo()" на "foo". но я все еще не получил ожидаемый результат:

foo() called

похоже на foo Функция вызывается только один раз.

Пожалуйста, помогите мне понять, почему это происходит.

4 ответа

Решение

Вы должны вернуть саму функцию-обертку, а не ее результат:

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc   # Do not call the function, return a reference instead

Декораторы заменяют декорированный предмет возвращаемым значением декоратора:

@tsfunc
def foo():
    # ....

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

def foo():
    # ....
foo = tsfunc(foo)

который расширяется до (в вашем коде):

foo = wrappedFunc()

так что вы заменяли функцию foo с результатом wrappedFunc() позвони, а не с wrappedFunc сам.

Вы должны удалить скобки в

return wrappedFunc

Декоратор должен возвращать функцию-обертку, а не вызывать ее.

С этим исправлением код выдает:

foo() called
foo() called

Немного поздно, но надеюсь, что это поможет, Продолжая объяснять, почему предоставленный ответ принят

в сообщении об ошибке "NoneType" означает, что экземпляр / объект, который мы пытаемся вызвать, не имеет типа (это не функция /int/boolean/class/instance). Его тип - просто "Нет". Итак, чтобы подвести итог, декораторы - это не что иное, как расширенное использование замыканий с функциями, рассматриваемыми как первоклассные граждане (вы можете получить подробное представление о замыканиях. Это в основном означает, что декоратор ожидает возврата функции, скажем, обертка в большинстве случаев, при этом исходная функция не изменяется и вызывается с помощью декоратора

def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc() -> Here the function is not returned but called eventually

@tsfunc
def foo():
    pass

foo() - > calling this again doesnt make sense to trigger the decorator, since a reference for the method foo is enough. Hence foo works fine but foo() doesn't (method call has completed already but no value is returned) If you try like this, you would see that the variable has 'None' type

def tsfunc(func):
        def wrappedFunc():
            print '%s() called' % func.__name__
            return func()
        return wrappedFunc --  Here I made the correction
    
    @tsfunc
    def foo():
        pass
var1 = foo()
print(var1)

Это то, что произошло с вызовом foo(), когда у вас был неправильный способ вызова функции-оболочки вместо возврата только функции

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

вы используете этот синтаксис

      def tsfunc(func):
    def wrappedFunc():
        print '%s() called' % func.__name__
        return func()
    return wrappedFunc # not use wrappedFunc() becaues Because this function runs at the this time 

@tsfunc
def foo():
    pass

foo()  # 
Другие вопросы по тегам