1

I have child classes which inherit some basic functionality from a parent class. The child classes shall have a generic constructor prepare_and_connect_constructor() which does some magic around the object creation of the parent class. For simplicity, the magic is done by a simple function based decorator (ultimately, it should be a part of the parent class).

def decorate_with_some_magic(func): def prepare_and_connect(*args, **kwargs): print("prepare something") print("create an object") obj = func(*args, **kwargs) print("connect obj to something") return obj return prepare_and_connect class Parent: def __init__(self, a): self.a = a def __repr__(self): return f"{self.a}" class Child(Parent): @classmethod @decorate_with_some_magic def prepare_and_connect_constructor(cls, a, b): """ use the generic connection decorator right on object creation """ obj = super().__init__(a) # put some more specific attributes (over the parents class) obj.b = b return obj def __init__(self, a, b): """ init without connecting """ super().__init__(a) self.b = b def __repr__(self): return f"{self.a}, {self.b}" if __name__ == '__main__': print(Child.prepare_and_connect_constructor("special child", "needs some help")) 

Using this code i finally get

obj = super().__init__(a) TypeError: __init__() missing 1 required positional argument: 'a' 

when running prepare_and_connect_constructor().

Actually I would expect that the super.__init__(a) call should be the same as in Child.__init__. I guess the reason is related to the classmethod but I can't figure it out.

What's wrong with this call?

Update: In general what was wrong is that __init__ doesn't return an object.

Due to the hints and thoughts from the answers i modified my code to achieve what i need:

class Parent: def __init__(self, a): self.a = a @staticmethod def decorate_with_some_magic(func): def prepare_and_connect(*args, **kwargs): print("prepare something") print("create an object") obj = func(*args, **kwargs) print("connect obj to something") return obj return prepare_and_connect def __repr__(self): return f"{self.a}" class ChildWithOneName(Parent): @classmethod @Parent.decorate_with_some_magic def prepare_and_connect_constructor(cls, a, b): """ use the generic connection decorator right on object creation """ obj = super().__new__(cls) obj.__init__(a, b) print("Does the same as in it's __init__ method") return obj def __init__(self, a, b): """ init without connecting """ super().__init__(a) self.b = b def __repr__(self): return f"{self.a}, {self.b}" class GodChild(Parent): @classmethod @Parent.decorate_with_some_magic def prepare_and_connect_constructor(cls, a, names): """ use the generic connection decorator right on object creation """ obj = super().__new__(cls) obj.__init__(a, names) # perform some more specific operations obj.register_all_names(names) print("And does some more stuff than in it's __init__ method") return obj def __init__(self, a, already_verified_names): """ init without connecting """ super().__init__(a) self.verified_names = already_verified_names def register_all_names(self, names=[]): self.verified_names = [] def verify(text): return True for name in names: if verify(name): self.verified_names.append(name) def __repr__(self): return f"{self.a}, {self.verified_names}" if __name__ == '__main__': print(ChildWithOneName.prepare_and_connect_constructor("special child", "needs some help"), end='\n\n') print(GodChild.prepare_and_connect_constructor("unknown child", "needs some verification"), end='\n\n') print(ChildWithOneName("my child", "is clean and doesn't need extra magic")) 
  • decorate_with_some_magic is now a part of the Parent class (using a staticmethod) as it is a related generic functionality
  • Each child class (added one more for illustration) has it's own prepare_and_connect_constructor classmethod, which calls its own constructor and does optionally some additional work
2
  • 2
    __init__ doesn't return anything, and since you are calling super from a class method, super().__init__ requires an explicit instance as its first argument. Commented Oct 7, 2019 at 18:56
  • True, didn't think about this Commented Oct 7, 2019 at 19:10

2 Answers 2

1

You have a slight misunderstanding of the magic methods __init__ and __new__. __new__ creates a new object, e.g. returns a instance of the class. __init__ just modifies the object in place. So an easy fix for your problem would be de following:

@classmethod @decorate_with_some_magic def prepare_and_connect_constructor(cls, a, b): """ use the generic connection decorator right on object creation """ obj = super().__new__(cls) obj.__init__(a) # put some more specific attributes (over the parents class) obj.b = b return obj 

I however don't think you should use it like this. Instead, your probably should overwrite __new__

Sign up to request clarification or add additional context in comments.

3 Comments

What should be the change to __new__ to do it correctly?
@maggie That would assume you always want to connect. Is that the case?
No, 's not, that is why i am asking. It should be an alternative (to write less code). But it's not always the case.
1

Decorators work on callables. Since there is no difference between calling a function and initiating a class, you can use your decorator directly on the class:

def decorate_with_some_magic(func): def prepare_and_connect(*args, **kwargs): print("prepare something") print("create an object") obj = func(*args, **kwargs) print("connect obj to something") return obj return prepare_and_connect class Parent: @classmethod def prepare_and_connect_constructor(cls, a, b): return decorate_with_some_magic(cls)(a, b) def __init__(self, a): self.a = a def __repr__(self): return f"{self.a}" class Child(Parent): def __init__(self, a, b): """ init without connecting """ super().__init__(a) self.b = b def __repr__(self): return f"{self.a}, {self.b}" if __name__ == '__main__': normal_child = Child("normal child", "no help needed") print(normal_child) special_child = Child.prepare_and_connect_constructor("special child", "needs some help") print(special_child) 

Output:

normal child, no help needed prepare something create an object connect obj to something special child, needs some help 

4 Comments

But i want to have a "normal" constructor and an alternative one. Thats not possible with that approach.
Use the decorator "outside" the class. See my modified answer.
Ok, this works and looks nice, but for multiple child classes there are different ways on how these classes work. Its not guaranteed that the same stuff happens as it does in the normal constructor... Perhaps its not the best design which i am thinking of, but isn't it possible to do it with a class method?
Now with class method.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.