pythonclasswrapperpython-decoratorsclass-decorator

Decorating a class with the correct names


I am trying to decorate a class

def decorate_module(Module):
    class Wrapper(Module):
        def __init__(self, cfg):
            self.c = create_c(**cfg)
            super().__init__()

    return Wrapper

While the code works, it gives a class named "Wrapper" instead of the name of the original module. When it comes to function decoration, one can use @wraps in this situation. I tried the same thing for class (by writing it just above class Wrapper(Module): but got an error.

What is the proper way to do this? A python built-in decorator @dataclass seems to be handling this well. I wonder how it works.. Is there a helper function like @wraps? Or should I manually overwrite __name__? If so, what are the full list of such "double-underscore "properties?


(EDIT) my original purpose

Given a class that does not take any arguments in __init__, I would like to decorate it to take an argument "cfg", do something (create_c), and store it as an attribute.

To be precise, my decorator will actually take "create_c" as an argument (I omitted this in the above code since additional nesting would make it verbose). Then, I am thinking of using it as follows.

@decorate_module(create_fn1)
class Module1:
    ...

@decorate_module(create_fn2)
class Module2:
    ...

Solution

  • There is no built-in equivalent to functools.wraps for a class, but you can copy yourself the dunder attributes of the class being wrapped, which include __doc__, __name__, __qualname__ and __module__, as documented.

    def decorate_module(Module):
        class Wrapper(Module):
            def __init__(self, cfg):
                self.c = create_c(**cfg)
                super().__init__()
    
        for attr in '__doc__', '__name__', '__qualname__', '__module__':
            setattr(Wrapper, attr, getattr(Module, attr))
        return Wrapper