Search code examples
pythondecoratorpython-decoratorsbuilt-in

Why do decorators not work with built in functions?


I am learning how to use decorators in Python, and I have a solid understanding of it, but I have one question - why can I not use decorators for a built-in function?

Why does this work:

def decorator1(func):
    def inner(*args, **kwargs):
        print("<>=========================<>")
        func(*args, **kwargs)
        print("<>=========================<>")
    return inner

@decorator1
def greet():
    print("Hello!")


greet()

and not this?:

def decorator1(func):
    def inner(*args, **kwargs):
        print("<>=========================<>")
        func(*args, **kwargs)
        print("<>=========================<>")
    return inner

@decorator1
print("Hello!")

Is it because the print function is executed on the spot, and the greet() function is only defined and is only ran after the @decorator1?


Solution

  • The syntax for @decorator can only be used together with a def ... function definition or class ... class definition statement. That doesn't mean you can't 'decorate' built-in functions.

    However, you tried to apply the syntax to an expression statement, one that calls the print() function. At most you'd decorate the return value (which for the print() function is always None).

    Decorators are just syntactic sugar, however. The syntax

    @decorator_expression
    def functionname(...): ...
    

    is executed as

    def functionname(...): ...
    
    functionname = decorator_expression(functionname)
    

    but without functionname being assigned to twice.

    So to decorate print, call the decorator explicitly:

    decorated_print = decorator1(print)
    decorated_print("Hello!")
    

    Note: I explicitly picked a different name here to assign the result of the decorator function to. You can use print = decorator1(print) too if you really want to. But then you may want to run del print later on to unmask the built-in function, or use builtins.print to access the original again.

    Demo:

    >>> def decorator1(func):
    ...     def inner(*args, **kwargs):
    ...         print("<>=========================<>")
    ...         func(*args, **kwargs)
    ...         print("<>=========================<>")
    ...     return inner
    ...
    >>> decorated_print = decorator1(print)
    >>> decorated_print("Hello!")
    <>=========================<>
    Hello!
    <>=========================<>