Gustavo Barros
Gustavo Barros

Reputation: 160

Accessing class property as decorator argument

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?

Upvotes: 1

Views: 1128

Answers (3)

chepner
chepner

Reputation: 530882

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

Upvotes: 1

quamrana
quamrana

Reputation: 39354

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()

Upvotes: 1

Maurice Meyer
Maurice Meyer

Reputation: 18106

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

Upvotes: 1

Related Questions