Search code examples
python-decorators

decorator syntax in python with same name for decorator and function to be decorated


How does the decorator syntax in python work when both the decorator and the function to be decorated have the same name? See code below

def say_hello(func):
    def wrapper(x):
        return x.upper()
    return wrapper

@say_hello
def say_hello(string):
    return string

say_hello('hello')

Here the name of the decorator and the name of the function to be decorated are the same..the code works fine and will return the decorated output 'HELLO'.

What I don't understand is when the second 'say_hello' function (the one to be decorated) is defined, why doesn't it immediately overwrite the first 'say_hello' function (the decorator) in the global namespace? How does the program still have access to the first 'say hello' function? Does the decorator syntax have its own namespace and the second 'say_hello' function is only defined in this namespace and not globally?


Solution

  • This is because when a decorator is declared on a function definition, the function name is not assigned to until the decorator is called with the new function object and returns the final function object.

    Excerpt from PEP-318:

    The current syntax for function decorators as implemented in Python 2.4a2 is:

    @dec2
    @dec1
    def func(arg1, arg2, ...):
        pass
    

    This is equivalent to:

    def func(arg1, arg2, ...):
        pass
    func = dec2(dec1(func))
    

    without the intermediate assignment to the variable func.

    Note the part where it says "without the intermediate assignment to the variable func". So in your case there is no intermediate variable name say_hello for the new function object. The new function object immediately after its construction is passed directly to a call to the decorator function say_hello, which returns the final function object that is then assigned to the name say_hello, overwriting the original say_hello that referred to the decorator. By that time we no longer need the original say_hello since the decorator is already applied to the new function say_hello.

    Also note that you should likely want to make the wrapper function return func(x.upper()) instead to make it an actual wrapper of func.