heuristicus
heuristicus

Reputation: 1138

Decorating class and its members after instantiation

I'm attempting to create some decorators which will allow class members to be decorated on instantiation, because I'd like to have some instances which are decorated and others which aren't.

In the example below, the intended outcome of applying the once decorator to an instance of SomeClass is that when some_func has been called, calling other_func prints a message rather than calling the original function.

#!/usr/bin/env python

import functools

def some_once(func):
    @functools.wraps(func)
    def wrapped(self, *args, **kwargs):
        if not self._new_attr:
            print("don't have new attr yet")
            func(self, *args, **kwargs)
            self._new_attr = True

    return wrapped

def other_once(func):
    @functools.wraps(func)
    def wrapped(self, *args, **kwargs):
        if self._new_attr:
            print("We have a new attr")
        else:
            func(self, *args, **kwargs)

    return wrapped

def once(cls):
    setattr(cls, "_new_attr", False)
    setattr(cls, "some_func", some_once(cls.some_func))
    setattr(cls, "other_func", other_once(cls.other_func))
    return cls

class SomeClass:

    def some_func(self, parameter):
        return "The parameter is " + str(parameter)

    def other_func(self, parameter):
        return "The other parameter is " + str(parameter)

if __name__ == '__main__':
    a = SomeClass()
    print(dir(a))
    print(a.some_func("p1"))
    print(a.other_func("p2"))

    b = once(SomeClass())
    print(dir(b))
    print(b.some_func("p3"))
    print(b.other_func("p4"))

The problem that results is that rather than looking at self._new_attr, the decorated functions instead look at string._new_attr, where the string is the parameter to the functions. I'm confused about what I'm doing wrong here.

Upvotes: 1

Views: 211

Answers (1)

Mike Müller
Mike Müller

Reputation: 85522

Don't decorate an instance. Make a new, decorated class.

Changing this line:

b = once(SomeClass())

into:

b = once(SomeClass)()

Should do the trick. once(SomeClass) gives you a new, decorated class. In the next step make an instance of it.

Upvotes: 1

Related Questions