Reputation: 1170
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?
Upvotes: 7
Views: 11414
Reputation: 1468
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
Upvotes: 4