1

I'm trying to apply a conditional decorator as described in another stackoverflow post, but I'd like the condition to be set from inside the class its being used. Instead I get a Reference error pointing that self is not defined.

class foo:
    def __init__(self):
        self.debug = True

    @conditional_decorator(decorator, self.debug)
    def function(self):
        pass

I tried defining a global variable and updating it from inside the __init__() method but it kept its original value when called as an argument of the decorator.

debug = None

class foo:
    def __init__(self):
        self.debug = True
        global debug
        debug = self.debug

    @conditional_decorator(decorator, debug)
    def function(self):
        pass

The only way it worked was declaring a global variable and setting it outside of the class.

How can I apply the value of the class property to the decorator?

Gustavo Barros
  • 150
  • 2
  • 7
  • btw `decorators` are applied as python reads the text of the program, not at runtime. – quamrana Mar 28 '20 at 21:42
  • 2
    @quamrana: No, decorators are applied at function definition time. – user2357112 Mar 28 '20 at 21:48
  • Ok, when is *function definition time*? – quamrana Mar 28 '20 at 21:49
  • 2
    Python function definitions are imperative - executing one generates a new function object. Decorators are applied immediately after this point. They are not and cannot be applied while Python is reading the source code; there is no function object to pass to the decorator at that point, and possibly no decorator to call either. – user2357112 Mar 28 '20 at 21:52
  • But surely function definition time occurs for each function *whilst* python is reading the source code (yes, just *after* reading the whole function), otherwise classes could refer to themselves by name. – quamrana Mar 28 '20 at 22:05
  • 1
    @quamrana When the `def` statement is executed, at run-time. – chepner Mar 28 '20 at 22:11
  • 1
    `class` statements are *also* executed at run-time, which is why the body of a class cannot refer to the class itself, because the class does not yet exist while the body is being executed. – chepner Mar 28 '20 at 22:27

3 Answers3

1

This is how you make a decorator handle classes and arguments:

from functools import wraps


def conditional_decorator(param):
    def real_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kw):
            cls = args[0]
            print(cls.debug)
            print(param)
        return wrapper
    return real_decorator


class foo:
    def __init__(self):
        self.debug = True

    @conditional_decorator('param1')
    def function(self):
        pass



f = foo()
f.function()

Output:

True
param1
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47
1

An update to the answer given by @Maurice Meyer which allows a member of the class to be nominated:

from functools import wraps

def conditional_decorator(decoration, member):
    def decorator(method):
        predecorated = decoration(method)
        @wraps(method)
        def wrapper(*args, **kwargs):
            self = args[0]
            condition = getattr(self, member)
            if not condition:
                return method(*args, **kwargs)
            return predecorated(*args, **kwargs)
         return wrapper
    return decorator

#And used like this for example:
class foo:
    def __init__(self, debug):
        self.debug = debug

    @conditional_decorator(decorator, "debug")
    def function(self):
        pass

f1 = foo(True)
f1.function()
quamrana
  • 37,849
  • 12
  • 53
  • 71
1

The decorator should not be conditional. Rather, when the decorated function is called, it should look at self.debug to determine whether to use the original function or the wrapped part.

def conditional_decorator(dec):
    def decorator(func):
        def _(self, *args, **kwargs):
            f = func
            if self.debug:
                f = dec(f)
            return f(self, *args, **kwargs)
        return _
    return decorator


def decorator(f):
    def _(*args, **kwargs):
        print("Decorated")
        return f(*args, **kwargs)
    return _


class foo:
    def __init__(self, debug):
        self.debug = debug

    @conditional_decorator(decorator)
    def function(self):
        print("foo stuff")

foo(True).function()
print("===")
foo(False).function()

outputs

Decorated
foo stuff
===
foo stuff
chepner
  • 497,756
  • 71
  • 530
  • 681