How to add more operations to a __init__() funcion of a class with a class-decorator?

Hi, guys,
I am a learner of python, and my question is how to add more operations to a existed __init__() funcion of a certain class with a class-decorator?

Your answer and guide will be appreciated!

The most natural solution is to add operations after cls() is called, like a normal function decorator:

def my_class_decorator(cls):
    @functools.wraps(cls)
    def create(*args, **kwargs):
        instance = cls(*args, **kwargs)
        instance.foo = 42  # more operations
        ...
        return instance
    return create
1 Like

Sorry, I don’t understand your question.

Can you give a simple example of what you mean?

Here is the specifical case:

class Parent:
    # We need to check whether the subclass has used too many 
    # memory after subclass's __init__()
    def __post_init__(self):
        print("here")
        assert self.memory() <= 1000_000_000

class ChildA:
    # We need to check whether the subclass has used too many 
    # memory after subclass's __init__()
    def _init__(self):
        print("Init ChildA")
        # use some memory
    # After _init__, automatically run the operations like "__post_init__"

class ChildB:
    # We need to check whether the subclass has used too many 
    # memory after subclass's __init__()
    def _init__(self):
        print("Init ChildB")
        # use some memory
    # After _init__, automatically run the operations like "__post_init__"

We hope there could be a way to check the memory use after the subclasses __init__() functions.

You don’t need a decorator for that. Use super() and inheritance:

class ChildA(Parent):
    def __init__(self):
        print("Init ChildA")
        # use some memory
        super().__init__(self)

By the way, you should not use assert the way you do:

# This is wrong.
assert self.memory() <= 1000_000_000

There is no guarantee that this line of code will run, and your library cannot control it. The user gets to control whether or not assertions run, but using Python with the -O “optimise” switch.

You can read more about assert here:

https://import-that.dreamwidth.org/676.html

Instead, you should use an if test, and raise a better exception:

if self.memory() > 1000_000_000:
    raise MemoryError("used too much memory")
1 Like

Thanks for your answer.
However, placing the super().__init__(self) to the last place, is not a common sense in our group, which might require good communication like documentation or tests.
In my opinion, decorator seems to be a better solution, which has no limit to the order of __init__() function.

Thank you sincerely for your solution, and we have realize our goal :smiley:

Your decorator solution is legal, but surprising or broken.

@my_class_decorator
class MyClass:
    pass

# This seems to work okay.
obj = MyClass()

# Now try this.
print( isinstance(obj, MyClass) )

Extending a class with new functionality is exactly what inheritance was invented for. Turning your class into a factory-function is hardly the “most natural solution”.

2 Likes

True, I should have stated that.

Another solution is to do monkey-patching stuff in the decorator.

Appreciate it very much for your persistence with solid analyses and good test case. I learned a lot from them. I am really sorry for my unexperienced comments above.
I also come up with an idea which solved the problem as:

def my_class_decorator(cls):
    class DecoratorClass(cls):
        @functools.wraps(cls, updated=())
        def __init__(self):
            super(DecoratorClass, self).__init__()
            self.foo = 42  # more operations
            ...    
    return DecoratorClass