Search code examples
pythondecorator

Error occuring when I try to run decorator with @ - Python


I am having a problem with the following programm. When I try to run decorator the easier way, using @ like this

def decorator1(fun):
    def wrapper():
        text = '------'
        return text + '\n' + fun + '\n' + text

    return wrapper()


def decorator2(fun):
    def wrapper():
        return fun.upper()

    return wrapper()

@decorator1
@decorator2
def function():
    return "Hey ya!"


print(function())

Following problems occurs:

Traceback (most recent call last):
  File "C:\Python_projects\main.py", line 17, in <module>
    def function():
  File "C:\Python_projects\main.py", line 13, in decorator2
    return wrapper()
  File "C:\Python_projects\main.py", line 11, in wrapper
    return fun.upper()
AttributeError: 'function' object has no attribute 'upper'

or when I switch the order of decorators then it goes like this:

Traceback (most recent call last):
  File "C:\Python_projects\main.py", line 17, in <module>
    def function():
  File "C:\Python_projects\main.py", line 6, in decorator1
    return wrapper()
  File "C:\Python_projects\main.py", line 4, in wrapper
    return text + '\n' + fun + '\n' + text
TypeError: can only concatenate str (not "function") to str

When I run the code in this way, then it works just fine:

def decorator1(fun):
    def wrapper():
        text = '------'
        return text + '\n' + fun + '\n' + text

    return wrapper()

def decorator2(fun):
    def wrapper():
        return fun.upper()

    return wrapper()

def function():
    return "Hey ya!"
 

print(decorator(decorator2(function())))

But it seems like using @ with decorators is much more popular. Do you have any idea what I am doing wrong?


Solution

  • A decorator is a function which is run on another function, as in a function object, not the result of another function. So:

    @decorator
    def func():
        ...
    

    Is equivalent to:

    def func():
        ...
    func = decorator(func)
    

    Notice that the function func has never actually been called.

    Therefore, within the wrapper function within the decorator, you actually need to call the function yourself. Use, for example, fun().upper(), not fun.upper().

    Also, the wrapper function should never be called within the decorator; the function itself should be returned, so return wrapper not return wrapper().

    Here is the corrected code:

    def decorator1(fun):
        def wrapper():
            text = '------'
            return text + '\n' + fun() + '\n' + text
    
        return wrapper
    
    
    def decorator2(fun):
        def wrapper():
            return fun().upper()
    
        return wrapper
    
    @decorator1
    @decorator2
    def function():
        return "Hey ya!"
    
    
    print(function())