1

I want to write a decorator that can be applied to single functions and to functions that are nested within objects. The difficulty is that I want the decorator to access object variables (via self.var) or function variables (via kwargs).

My sample code to decorate a single function looks like:

def outer_decorator(var_to_print):
    def inner_decorator(function):
        def wrapper(*args, **kwargs):
            if var_to_print in kwargs.keys():
                string_to_print = kwargs.get(var_to_print, "")
                print(string_to_print)
            return function(*args, **kwargs)
        return wrapper
    return inner_decorator

@outer_decorator(var_to_print='var')
def print_something(var=''):
    print('print_second')

print_something(var = 'print_first')
#print_first
#print_second

For objects I want to do something similar but instead of accessing kwargs, I would like to access self.var:

        def wrapper(self, *args, **kwargs):
            string_to_print = getattr(self, var_to_print)
            print(string_to_print)
            return function(self, *args, **kwargs)

Any ideas on how to check dynamically what wrapper should be applied? I tried to check whether it is a callable, but that seems to apply to both.

3
  • How are you using this decorator with objects? What did you try? Commented Jan 31, 2020 at 9:09
  • ` I tried to check whether it is a callable, but that seems to apply to both.` ^ can you give an example? I'm trying to do callable(var_to_print) and it gives me False as I would expect... Commented Jan 31, 2020 at 9:13
  • @yedpodtrzitko: The question is rather whether callable(normal_function) and callable(function_of_object) evaluate to true, which they both do Commented Jan 31, 2020 at 10:04

1 Answer 1

3

You can use isfunction from the inspect module.

Return True if the object is a Python function, which includes functions created by a lambda expression.

>>> from inspect import isfunction
>>> class A():
...     pass
... 
>>> def f():
...     pass
... 
>>> a = A()
>>> isfunction(a)
False
>>> isfunction(f)
True

Actually, what you want to do is to distinguish between functions and methods by using the same decorator for both. A method is still a function. It is considered a method when you access it from the class or from an instance of the class.
There are two options I can think of.

  1. Add an argument to the decorator @outer_decorator(var_to_print='var', f_type='method')
  2. use inspect.getfullargspec under the assumption that in your class you would use self as parameter name and your functions will not. Example:

    if 'self' in inspect.getfullargspec(f)[0]:
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.