Oily
Oily

Reputation: 682

Decorating class method at runtime

I want to decorate a class method, but at runtime (by that I mean that the decorator does not need to be specified before the method with the @ notation)

See this example of a standard method decorator:

def method_decorator(func):
    def decorated(self, *args, **kwargs):
        print('decorator:', args, kwargs)
        return func(self,*args, **kwargs)
    return decorated

class Standard():
    @ method_decorator
    def decorated(self, *args, **kwargs):
        print('decorated: ', args, kwargs)

s = Standard()
s.decorated(1,2)

Result:

decorator: (1, 2) {}
decorated:  (1, 2) {}

So I tried different ways to do it at runtime:

class RunTime():
    def set_decorator_1(self, decorator):
        self.decorated = decorator(self.decorated)
    def set_decorator_2(self, decorator):
        RunTime.decorated = decorator(RunTime.decorated)
    def set_decorator_3(self, decorator):
        self.decorated = decorator(RunTime.decorated)
    def set_decorator_4(self, decorator):
        setattr(self, 'decorated', decorator(RunTime.decorated))
    def set_decorator_5(self, decorator):
        setattr(self, 'decorated', decorator(self.decorated))
    def decorated(self, *args, **kwargs):
        print('decorated: ', args, kwargs)

r = RunTime()
r.set_decorator_*(method_decorator)
r.decorated(1,2)

And here are the outputs:

  1. The decorator is not properly decorated:
decorator: (2,) {}
decorated:  (1, 2) {}
  1. Works as expected, but when set_decorator is called, all RunTime instances are also decorated, which I want to avoid, because I only want to decorate the method of a single instance.
  2. Bad decoration
decorator: (2,) {}
decorated:  (2,) {}
  1. same as 3
  2. same as 1

I also tries another decorator, which works well (with set_decorator_1) but does not allow me to access self in it:

def method_decorator_runtime(func):
    def decorated( *args, **kwargs):
        print('decorator: ', args, kwargs)
        return func( *args, **kwargs)
    return decorated

Does anyone know a proper way of decorating a method at run time, with ability to access self in the decorator ?

Upvotes: 0

Views: 482

Answers (1)

EvensF
EvensF

Reputation: 1610

The closest solution I have found for your question is to use a decorator with an argument where you can pass the instance of the object that has its method decorated.

def method_decorator(self_eventually):
    def decorator(func):
        def decorated(*args, **kwargs):
            print('decorator:', repr(self_eventually), args, kwargs)
            return func(*args, **kwargs)
        return decorated
    return decorator

class RunTime():

    def set_decorator(self, decorator):
        self.decorated = decorator(self)(self.decorated)

    def decorated(self, *args, **kwargs):
        print('decorated:', repr(self), args, kwargs)

r = RunTime()
r.set_decorator(method_decorator)
r.decorated(1, 2)

Upvotes: 1

Related Questions