Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging Python decorators with arguments into a single one [duplicate]

I'm using two different decorators from two different libraries. Lets say: @decorator1(param1, param2) and @decorator2(param3, param4). That I often use in many functions as:

from moduleA import decorator1
from moduleB import decorator2

@decorator2(foo='param3', bar='param4')
@decorator1(name='param1', state='param2')
def myfunc(funcpar1, funcpar2):
    ...

Since it happens every time, I would like to create a custom decorator that handle both of them. Something like:

@mycustomdecorator(name='param1', state='param2',
                   foo='param3', bar='param4')
def myfunc(funcpar1, funcpar2):
    ...

How do I achieve that?

like image 534
Lin Avatar asked Sep 26 '18 12:09

Lin


People also ask

How do I combine multiple decorators in Python?

In Python, the decorator syntax is a shortcut for calling the decorator and storing its result in the same name. For example, this snippet: @login_required def index(request): ... Therefore to combine these decorators, you can implement a new function that calls each of the decorators in turn like this.

What is a decorator with arguments in Python?

A decorator with arguments is defined as a function that returns a standard decorator. This is actually very hard to think about, so I'm going to show you how to extend the previous decorator to accept one argument: The inner_decorator function in this example is almost identical to the my_decorator () function in the previous one.

How to decorate a function multiple times in Python?

A function can be decorated multiple times. We need to define the decorator first that we want to wrap the output string with, and then apply them to the function using the ‘@’ . One simply needs to place the decorators above the desired function. Nested decorators follow a bottom to top approach i.e the reverse order.

What is the difference between decorator and even function in Python?

Even function is a type of object in Python. Decorators are a special type of function which return a wrapper function. They are considered very powerful in Python and are used to modify the behaviour of a function temporarily without changing its actual value. Nesting means placing or storing inside the other.


1 Answers

I would argue that you shouldn't - using the original names for the decorators gives much better readability.

However, if you really want to, you can do it like this:

import functools

from moduleA import decorator1
from moduleB import decorator2

def my_decorator(foo, bar, name, state):
    def inner(func):
        @decorator2(foo=foo, bar=bar)
        @decorator1(name=name, state=state)
        @functools.wraps(func)  # Not required, but generally considered good practice
        def newfunc(*args, **kwargs)
            return func(*args, **kwargs)
        return newfunc
    return inner

@my_decorator(foo='param3', bar='param4', name='param1', state='param2')
def myfunc(funcpar1, funcpar2):
    ...

Based on comments though, here's an alternative method:

def my_decorator(foo, bar, name, state):
    def inner(func):
        # Please note that for the exact same result as in the other one, 
        # the order of decorators has to be reversed compared to normal decorating
        newfunc = decorator1(name=name, state=state)(func)
        newfunc = decorator2(foo=foo, bar=bar)(newfunc)
        # Note that functools.wraps shouldn't be required anymore, as the other decorators should do that themselves
        return newfunc
    return inner

To some, this might look simpler. However, people experienced with Python are used to decorators being applied with an @ - and even for that reason alone, I like my first option better. I know I'd take three times as long to read this code for the first time and understand what it does.

It's simple really - just write a decorator that returns another decorator which will have it's inner function decorated with the other two decorators ;)

It might also be a good idea to use functools.wraps, for the sake of good habits. It's standard library and helps a lot with debugging and interactive console use: https://docs.python.org/3.7/library/functools.html

In general though, I'd say the one extra line of code is more than worth the clarity of using the decorators separately. You'll thank yourself when you read your own code in 3 more months.

like image 126
Gloweye Avatar answered Oct 14 '22 06:10

Gloweye