Decorate a class in Python by defining the decorator as a class

27,862

Solution 1

If you want to overwrite new_method(), just do it:

class Decorator(object):
    def __init__(self, arg):
        self.arg = arg
    def __call__(self, cls):
        class Wrapped(cls):
            classattr = self.arg
            def new_method(self, value):
                return value * 2
        return Wrapped

@Decorator("decorated class")
class TestClass(object):
    def new_method(self, value):
        return value * 3

If you don't want to alter __init__(), you don't need to overwrite it.

Solution 2

After this, the class NormalClass becomes a ClassWrapper instance:

def decorator(decor_arg):

    class ClassWrapper:
        def __init__(self, cls):
            self.other_class = cls

        def __call__(self,*cls_ars):
            other = self.other_class(*cls_ars)
            other.field += decor_arg 
            return other

    return ClassWrapper

@decorator(" is now decorated.")
class NormalClass:
    def __init__(self, name):
        self.field = name

    def __repr__(self):
        return str(self.field)

Test:

if __name__ == "__main__":

    A = NormalClass('A');
    B = NormalClass('B');

    print A
    print B
    print NormalClass.__class__

Output:

A is now decorated. <br>
B is now decorated. <br>
\__main__.classWrapper
Share:
27,862

Related videos on Youtube

tsps
Author by

tsps

Updated on January 04, 2020

Comments

  • tsps
    tsps over 4 years

    What is a simple example of decorating a class by defining the decorator as a class?

    I'm trying to achieve what has been implemented in Python 2.6 using PEP 3129 except using classes not functions as Bruce Eckel explains here.

    The following works:

    class Decorator(object):
        def __init__(self, arg):
            self.arg = arg
    
        def __call__(self, cls):
            def wrappedClass(*args):
                return cls(*args)
            return type("TestClass", (cls,), dict(newMethod=self.newMethod, classattr=self.arg))
    
        def newMethod(self, value):
            return value * 2
    
    @Decorator("decorated class")
    class TestClass(object):
        def __init__(self):
            self.name = "TestClass"
            print "init %s"%self.name
    
        def TestMethodInTestClass(self):
            print "test method in test class"
    
        def newMethod(self, value):
            return value * 3
    

    Except, in the above, wrappedClass is not a class, but a function manipulated to return a class type. I would like to write the same callable as follows:

    def __call__(self, cls):
            class wrappedClass(cls):
                def __init__(self):
                    ... some code here ...
            return wrappedClass
    

    How would this be done?

    I'm not entirely sure what goes into """... some code here ..."""

    • Sven Marnach
      Sven Marnach over 12 years
      Did you try the code you posted yourself? It should work.
    • tsps
      tsps over 12 years
      The first part using the function does work. How would I write wrappedClass as a genuine class though?
    • Sven Marnach
      Sven Marnach over 12 years
      What is your decorator supposed to do? I cannot tell you what code has to go into "some code here" without knowing what this code is supposed to do.
    • tsps
      tsps over 12 years
      I want to achieve what can be achieved with a function using a class. I understand this can be done, but couldn't find any examples to illustrate this
    • Sven Marnach
      Sven Marnach over 12 years
      I don't get it. The code in your second snippet should work as it is. You can put just any code where "some code here" is. How am I supposed to now what this code is supposed to do? If you don't want to overwrite __init__(), simply don't. If you do want to overwrite it, you obviously want to change its behaviour in some way. I'm asking: In what way?
    • tsps
      tsps over 12 years
      The decorator should alter the method "newMethod" in TestClass which triples values to a method that just doubles values. In other words it has to replace the method in TestClass. I get my panties in a bunch soon after I declare __init__(self) and get confused by the Decorator's reference to itself, the class (cls) I am decorating and the wrappedClass's self. HTH clear the confusion
  • Melvic Ybanez
    Melvic Ybanez over 9 years
    you forgot to return the 'other' variable inside the call method
  • han shih
    han shih almost 4 years
    Is there any scenario that we should decorate class instead of using class inheritance?