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?
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.
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.
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With