Search code examples
pythonpython-3.xdecoratorpython-decorators

Can I run a decorated function without a decorator functionality?


If I once used a decorator to my function, how could I run this function alone, without being embedded in a decorator functionality?

For example I have a function printArg and it prints an argument. For some usability I needed to "mix it" with datetime.now(). For that I wrote a decorator timeCalled. Now every time I call printArg also a decorator called.

Is there a way to call printArg alone, so I wouldn't repeat myself and wrote another "printArg" without decorator functionality(datetime.now())?

from datetime import datetime

def timeCalled(func):
    def wrapper(*args, **kwargs):
        print(f'{datetime.now()}: Function called {func.__name__}')
        result = func(*args, **kwargs)
        return result
    return wrapper

@timeCalled
def printArg(arg):
    print(f'Your arg is {arg}')

printArg('Mama')

Solution

  • There is no general way that doesn't rely on implementation details. In this particular case, since func references the original function in wrapper and is in the closure, you can use:

    printArg.__closure__[0].cell_contents
    

    To retrieve it.

    More generally, it is good practice to use functools.wraps when you create the wrapper, this makes the wrapper function "look like" the original function. Moreover, it adds the original function to a __wrapped__ attribute, so:

    from datetime import datetime
    from functools import wraps
    
    def timeCalled(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f'{datetime.now()}: Function called {func.__name__}')
            result = func(*args, **kwargs)
            return result
        return wrapper
    
    @timeCalled
    def printArg(name):
        print(f'Your arg is {name=}.')
    

    In which case, you can use:

    printArg.__wrapped__