Skip to content Skip to sidebar Skip to footer

Class Decorator For Methods From Other Class

NOTE: I've got a related question here: How to access variables from a Class Decorator from within the method it's applied on? I'm planning to write a fairly complicated decorator

Solution 1:

Functions are descriptors and that's what allows them to auto-bind self. The easiest way to deal with this is to implement decorators using functions so that this is handled for you. Otherwise you need to explicitly invoke the descriptor. Here's one way:

import functools


classMyDecoratorClass:
    def__init__(self, method):
        functools.update_wrapper(self, method)
        self.method = method

    def__get__(self, instance, owner):
        returntype(self)(self.method.__get__(instance, owner))

    def__call__(self, *args, **kwargs):
        # do stuff before
        retval = self.method(*args, **kwargs)
        # do stuff afterreturn retval


classFoobar:
    def__init__(self):
        # initialize stuffpass    @MyDecoratorClassdeffoo(self, x, y):
        print(f"{[self, x, y]=}")


@MyDecoratorClassdefbar(spam):
    print(f"{[spam]=}")


Foobar().foo(1, 2)
bar(3)

Here the __get__ method creates a new instance of MyDecoratorClass with the bound method (previously self.method was just a function since no instance existed yet). Also note that __call__ just calls self.method(*args, **kwargs) - if self.method is now a bound method, the self of FooBar is already implied.

Solution 2:

You can implement the descriptor protocol, an example of how functions do it (but in pure python) is available in the Descriptor HOWTO, translated to your case:

import functools
import types

classMyDecoratorClass:
    def__init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func

    def__call__(self, *args, **kwargs):
        # do stuff before
        retval = self.func(*args, **kwargs)
        # do stuff afterreturn retval

    def__get__(self, obj, objtype=None):
        if obj isNone:
            return self
        return types.MethodType(self, obj)

Note, return types.MethodType(self, obj) is essentially equivalent to

returnlambda *args, **kwargs : self.func(obj, *args, **kwargs)

Note from Kristof Could it be that you meant this:

return types.MethodType(self, obj) is essentially equivalent to

returnlambda *args, **kwargs : self(obj, *args, **kwargs)

Note that I replaced self.func(..) with self(..). I tried, and only this way I can ensure that the statements at # do stuff before and # do stuff after actually run.

Post a Comment for "Class Decorator For Methods From Other Class"