Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A decorator that returns multiple functions

Tags:

I'd like to write a decorator that places multiple functions into the module namespace. Consider the following module:

# my_module.py


from scipy import signal


@desired_decorator(new_size=(8, 16, 32))
def resample(x, new_size):
    return signal.resample(x, new_size)

I'd like to now be able to import resample_8, resample_16, and resample_32 from my_module. I can write the decorator and have it return a list of functions, but how can those functions be made available in the module namespace?

like image 692
Kyle Avatar asked Jun 23 '19 00:06

Kyle


People also ask

Can a function have multiple decorators?

Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.

What is the return type of a decorator?

A decorator is a callable (usually a function though sometimes a class) that accepts either a function or a class and returns a new function or class that wraps around the original one.

What are decorator functions?

By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.

What are the types of decorators in Python?

In fact, there are two types of decorators in Python — class decorators and function decorators — but I will focus on function decorators here. Before we get into the fun details of how a basic decorator works and how to implement your own decorators, let's see why we need them in the first place.


1 Answers

Due to the fact that you can assign to the global dictionary without using sneaky hacks, this is just almost possible. (grammar nice)

EDIT: K, maybe it's a lil bit sneaky. Don't try this at home without a supervising Pythonista. martineau

EDIT2: It is possible to get the caller's globals by using stack introspection, which avoids the importing problem, but it won't work when invoked in a non-global namespace, or dissipate your confusion in 6 months. user2357112

globals() returns a dictionary of the global variables. Assigning to this makes it possible for a user to import these functions

functools.partial is a great way to make partial functions. This basically makes a 'half complete' function call. Creating a partial function makes it remember the arguments and keyword arguments and calling that partial function will call the original function with the arguments and keyword arguments. Read more about it here.

Here's the decorator you want, though I would strongly suggest against using this.

from functools import partial

def desired_decorator(**kwargs):
    # make sure there's only one keyword argument
    assert len(kwargs) == 1
    # unpack the single keyword and the values
    keyword, values = (*kwargs.items(),)[0]
    # this is the actual decorator that gets called
    def _make_variants(func):
        for value in values:
            # assign to the globals dictionary
            globals()[
                f"{func.__name__}_{value}"
            ] = partial(func, **{keyword: value})
        # keep the original function available
        return func
    return _make_variants

My alternative would be to use what Chris said as creating many functions from a decorator would not be good for maintenance and for clarity.

Here's the code I suggest, but you can use the one above if you want.

from functools import partial

# assign your function things here
resample_8 = partial(resample, new_size=8)
# repeat for other names
like image 89
GeeTransit Avatar answered Sep 22 '22 09:09

GeeTransit