1

I write a decorator for class method

def decor(method):
    def wrapped(self, *args, **kwargs):
        return method(self, *args, **kwargs)
    # [*]
    return wrapped

I would like use this like:

class A(metaclass=mymetaclass):
    @decor
    def meth(self):
        pass

How I can in decorator add method/variable to class which has decorated method? I need it do near [*]. Inside wrapped I could write self.__class__, but what to do here?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You can use `method.im_class` http://stackoverflow.com/questions/7680446/get-python-functions-owning-class-from-decorator – name_no Dec 12 '15 at 16:32
  • You didn't really care about it being an actual @classmethod right? Reading your question it doesn't look like it. – Lincoln Nov 05 '20 at 17:12

5 Answers5

5

I cannot imagine a way to meet such a requirement, because decor function only receives a function object that knows nothing about a containing class.

The only workaround that I can imagine is to use a parameterized decorator and pass it the class being decorated

def decor(cls):
    def wrapper(method):
        def wrapped(self, *args, **kwargs):
            return self.method(*args, **kwargs)
        print method   # only a function object here
        return wrapped
    print cls  # here we get the class and can manipulate it
    return wrapper

class A
    @decor(A)
    def method(self):
        pass

Alternatively, you could decorate the class itself:

def cdecor(cls):
    print 'Decorating', cls  # here we get the class and can manipulate it
    return cls

@cdecor
class B:
    def meth(self):
        pass

gives:

Decorating __main__.B
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

It looks like you just wanted to decorate one of a classes functions, not specifically an @classmethod. Here's a simple way that I did it when I wanted to call a classes save function when the function returned a successful result:

def save_on_success(func):
    """ A decorator that calls a class object's save method when successful """
    def inner(self, *args, **kwargs):
        result = func(self, *args, **kwargs)
        if result:
            self.save()
        return result
    return inner

Here is an example of how it was used:

class Test:
    def save(self):
            print('saving')
    @save_on_success
    def test(self, var, result=True):
            print('testing, var={}'.format(var))
            return result

Testing to make sure it works as expected:

>>> x = Test()
>>> print(x.test('test True (should save)', result=True))
testing, var=test True (should save)
saving
True
>>> print(x.test('test False (should not save)', result=False))
testing, var=test False (should not save)
False
Lincoln
  • 1,008
  • 12
  • 20
0

It looks like it is not directly possible, according to this response :

Get Python function's owning class from decorator

What you could do instead is providing a decorator for your class, something like that :

class InsertMethod(object):
    def __init__(self, methodToInsert):
        self.methodToInsert = methodToInsert

    def __call__(self, classObject):
        def wrapper(*args, **kwargs):
            setattr(classObject, self.methodToInsert.__name__, self.methodToInsert)
            return classObject(*args, **kwargs)
        return wrapper

def IWillBeInserted(self):
    print "Success"


@InsertMethod(IWillBeInserted)
class Something(object):
    def __init__(self):
        pass

    def action(self):
        self.IWillBeInserted()


a = Something()
a.action()
Community
  • 1
  • 1
Mathieu C.
  • 779
  • 5
  • 10
0

Actually, you may decorate the class itself:

def class_decorator(class_):
    class_.attribute = 'value'
    class_.method = decorate(class_.method)
    return class_

@class_decorator
class MyClass:
    def method(self):
        pass
name_no
  • 354
  • 2
  • 9
0

I'm a little late to the party, but late is better than never eh? :)

We can do this by decorating our class method with a decorator which is itself a class object, say B, and then hook into the moment when Python calls B.__get__ so to fetch the method. In that __get__ call, which will be passed both the owner class and the newly generated instance of that class, you can elect to either insert your method/variable into the original owner class, or into the newly defined instance.

class B(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kwargs):
        return self.f(*args, **kwargs)

    def __get__(self, instance, owner):

        instance.inserted = True
        # owner.inserted = True
        def wrapper(*args, **kwargs):
            return self(instance, *args, **kwargs)
        return wrapper

class A:
    @B
    def method(self):
        pass

if __name__ == "__main__":
    a = A()
    a.method()
    b = A()

    print(hasattr(a, 'inserted'))
    print(hasattr(b, 'inserted'))

In this example, we're wrapping def method(self) with @B. As written, the inserted attribute inserted will only persist in the a object because it's being applied to the instance. If we were to create a second object b as shown, the inserted attribute is not included. IE, hasattr(a, 'inserted') prints True and hasattr(b, 'inserted') prints False. If however we apply inserted to the owner class (as shown in the commented out line) instead, the inserted attribute will persist into all future A() objects. IE hasattr(a, 'inserted') prints True and hasattr(b, 'inserted') prints True, because b was created after a.method() was called.

Michael Green
  • 719
  • 6
  • 15