Applying a decorator to an imported function?

14,009

Decorators are just syntactic sugar to replace a function object with a decorated version, where decorating is just calling (passing in the original function object). In other words, the syntax:

@decorator_expression
def function_name():
    # function body

roughly(*) translates to:

def function_name():
    # function body
function_name = decorator_expression(function_name)

In your case, you can apply your decorator manually instead:

from random import randint

randint = decorator(randint)

(*) When using @<decorator> on a function or class, the result of the def or class definition is not bound (assigned to their name in the current namespace) first. The decorator is passed the object directly from the stack, and only the result of the decorator call is then bound.

Share:
14,009
killajoule
Author by

killajoule

Updated on June 06, 2022

Comments

  • killajoule
    killajoule about 2 years

    I want to import a function:

    from random import randint
    

    and then apply a decorator to it:

    @decorator
    randint
    

    I was wondering if there was some syntactic sugar for this (like what I have above), or do I have to do it as follows:

    @decorator
    def randintWrapper(*args):
        return random.randint(*args)
    
  • pratikm
    pratikm over 8 years
    The only problem is that the docstring and method name is not preserved. Maybe want to use functools.wraps
  • Martijn Pieters
    Martijn Pieters over 8 years
    @pratikm: that's a separate issue though. The decorator should indeed use functools.wraps, but that doesn't change how can you apply the decorator.
  • CMCDragonkai
    CMCDragonkai almost 6 years
    This doesn't seem to work for flask route decorators. That is @app.route('/route', methods=['GET']).
  • Martijn Pieters
    Martijn Pieters almost 6 years
    @CMCDragonkai: it works jut fine for Flask route decorators, provided you call the result of the decorator factory: app.route('/route', methods=['GET'])(view_func) (no need to capture the return value, since the app.route() decorator registers, and returns the view function unaltered). Not that you should do that however, since you could just use app.add_url_rule() instead: app.add_url_rule('/route', view_func.__name__, view_func, methods=['GET']).
  • Corey Levinson
    Corey Levinson almost 4 years
    The clearest example I have seen so far. Note that your decorator itself may be a called function rather than the function name. E.g. I had to do torch.cuda.amp.autocast()(model.forward) It is weird because there are 2 parentheses next to each other, but that is the consequence of being a programmer.
  • Martijn Pieters
    Martijn Pieters almost 4 years
    @CoreyLevinson the term for those is a decorator factory. Like the Flask route decorator factory, it’s the call that returns the actual decorator. This is generally used to configure the decorator that’s being produced. E.g. autocast() takes an enabled parameter.
  • Corey Levinson
    Corey Levinson almost 4 years
    @MartijnPieters Thank you for that insight, I have never heard of the term "decorator factory" before. There is much to learn still!