0

I have a library proving the following code:

class LibClass:
    def decorator_method(self, *args, **kwargs):
        # some code...
        pass

which is intended to be used as follows:

k = LibClass()
method_args = (,) # something I create

@k.decorator_method(method_args)
def my_func():
    # Code that I want to run
  

Specifically, the library implements some callback-like functionality via these decorators. Naturally, I have no control over the library, nor is there any alternative functionality.

Importantly, the instance of LibClass is part of the decorator, so my_func is added to the callbacks of that very instance, k.

However, I have implemented my_func elsewhere in my code and I want to separate the function from the decoration. One way would be to create a wrapper like so:

k = LibClass()

@k.decorator_method(method_args)
def my_func_wrapper():
    # Actual call
    my_func()

This approach works the way I want, but it has the disadvantage of having to define and call the wrapper function which is not strictly necessary. However, I cannot apparently apply the decorator directly, since

def my_func():
    # ...

@k.decorator_method(method_args)
my_func

is not valid python code. Similarly, I might think about decorating a closure in this way, like

def my_func_producer(more_args):
    def my_func():
        # Do something with more_args

    return my_func

where

more_args = (,)

@k.decorator_method(method_args)
my_func_producer(more_args)

is also invalid. Is there any way to apply the decorator without having to define an additional wrapper function?

hfhc2
  • 4,182
  • 2
  • 27
  • 56
  • Does this answer your question? [how to do a conditional decorator in python](https://stackoverflow.com/questions/10724854/how-to-do-a-conditional-decorator-in-python) – ddejohn Sep 21 '21 at 17:15
  • "I cannot apparently apply the decorator directly," of course you can. After you define `myfunc`, just do `myfunc = k.decorator_method(method_args)(myfunc)`. This is what the `@` syntactic sugar *is doing in the first place*. – juanpa.arrivillaga Sep 21 '21 at 17:19
  • Decorator syntax is just a shortcut. if `my_func` is already defined, you can simply write `my_func = k.decorator_method(method_args)(my_func)`. If your decorator doesn't actually create a new function, only registering it with `k`, you can omit the assignment. – chepner Sep 21 '21 at 17:20
  • as an aside, I believe you want `k.decorator_method(*method_args)` instead of just `k.decorator_method(method_args)` – juanpa.arrivillaga Sep 21 '21 at 17:20

1 Answers1

2

Instead of this:

def my_func():
    # ...

@k.decorator_method(method_args)
my_func

do this:

def my_func():
    # ...

my_func = k.decorator_method(method_args)(my_func)

This is doing exactly what the decorator does, but for a minor detail. The "@" syntax for decorators was introduced back in version 2.3 as a syntax sugar to just that: the decorator callable (placed after the @) is called with the function defined starting on the next line as its sole parameter.

So, if we just call the same decorator with the usual calling syntax, passing the function itself as parameter, the effect is the same. In this case we have to rebind the decorated object to the function name, hence the my_func = ... before the call. (the @syntax does an impplicit name binding and that is the "minor detail" I mentioned on the first paragraph)

jsbueno
  • 99,910
  • 10
  • 151
  • 209