Is there a way to create multiple decorators with the same naming convention in Python?
Question:
We have a few decorators to register functions and classes inside imported files, that look like the following:
def register_crawler(func):
register(func, "crawler")
return func
def register_model(func):
register(func, "model")
return func
def register_generator(func):
register(func, "generator")
return func
In each case, we are calling another helper function that will actually perform the registraion, i.e. storing a reference to the imported object.
We have several of these decorators, registering different components. Is there a way to define a single decorators with a naming convention, plus a list of all components, so we can do something like:
components = ["crawler", "model", "generator", ...]
for variable in components:
def register_[variable](func)
register(func, variable)
return func
One obvious solution is to instead define a decorator that takes the component type, e.g. crawler
, model
, etc as an argument, have a validator to check if the provided name matches supported components, then call the helper function. But we would lose the explicit @register_crawler
decorator, and we would prefer to keep @register_crawler
; @register_model
, etc.
Answers:
This seems like a perfect use-case for a partial
to me:
from functools import partial
def register_for(func, *, component):
register(func, component)
return func
register_crawler = partial(register_for, component="crawler")
register_model = partial(register_for, component="model")
register_generator = partial(register_for, component="generator")
partial
has the effect of "pinning" some of the arguments to the wrapped function, which simplifies it’s parameterization. Hence, you may now use your functions as simple decorators, as you hoped:
@register_crawler
def crawl_something(*a, **kw):
...
without rewriting the same logic over and over again.
We have a few decorators to register functions and classes inside imported files, that look like the following:
def register_crawler(func):
register(func, "crawler")
return func
def register_model(func):
register(func, "model")
return func
def register_generator(func):
register(func, "generator")
return func
In each case, we are calling another helper function that will actually perform the registraion, i.e. storing a reference to the imported object.
We have several of these decorators, registering different components. Is there a way to define a single decorators with a naming convention, plus a list of all components, so we can do something like:
components = ["crawler", "model", "generator", ...]
for variable in components:
def register_[variable](func)
register(func, variable)
return func
One obvious solution is to instead define a decorator that takes the component type, e.g. crawler
, model
, etc as an argument, have a validator to check if the provided name matches supported components, then call the helper function. But we would lose the explicit @register_crawler
decorator, and we would prefer to keep @register_crawler
; @register_model
, etc.
This seems like a perfect use-case for a partial
to me:
from functools import partial
def register_for(func, *, component):
register(func, component)
return func
register_crawler = partial(register_for, component="crawler")
register_model = partial(register_for, component="model")
register_generator = partial(register_for, component="generator")
partial
has the effect of "pinning" some of the arguments to the wrapped function, which simplifies it’s parameterization. Hence, you may now use your functions as simple decorators, as you hoped:
@register_crawler
def crawl_something(*a, **kw):
...
without rewriting the same logic over and over again.