Is it possible to prohibit the init call?

I am editing the original question because we are all focused on the SHOULD you ever do this. My question is simply whether I CAN do it and HOW (realizing that there can be several solutions). So I'm just going to leave the real question and cut out the background.

Suppose I have a base class and a child class. Is there anything I can do in the base class to prevent __init__ from calling the child class - or at least throw an exception or even a log if __init__ exists or is called in the child class? I want the __init__ method to be called in the parent class.

Change / Conclusion . After examining the options presented in the answers, I decided that it would be a bad style. I will solve my problem differently. However, I hope the answers below are useful if someone wants to do this.

+4
source share
2 answers

This is perfectly doable, but I don't think you need it. Tell users how to use their class, and they must obey. Also, if someone is a subclass, he needs to know how to call the parent initialization method.

As a proof of concept, here's how to do it with metaclasses (Python 2.x syntax):

>>> class WhoMovedMyInit(object): class __metaclass__(type): def __init__(self, *args, **kw): super(type,self).__init__(*args, **kw) if self.__init__ is not WhoMovedMyInit.__init__: raise Exception('Dude, I told not to override my __init__') >>> class IAmOk(WhoMovedMyInit): pass >>> class Lol(WhoMovedMyInit): def __init__(self): pass Traceback (most recent call last): File "<pyshell#35>", line 1, in <module> class Lol(WhoMovedMyInit): File "<pyshell#31>", line 6, in __init__ raise Exception('Dude, I told not to override my __init__') Exception: Dude, I told not to override my __init__ 

You can also replace the subclass __init__ method with one that warns the user or causes an error in "runtime".

+9
source

"Should I do this or not, this is a separate discussion :)"

Please keep this in mind.

But this can be done - when the class instance is created, not only the syntax is similar to a method call - with the name of the class object, followed by the y bracket - the class itself (which is a Python object), as the called object.

Calling an object in Python calls the __call__ magic method in its class. Therefore, creating an instance of the class, it calls the __call__ method in its metaclass.

What is inside this __call__ method in a standard metaclass (which is a "type") is roughly equivalent:

 def __call__(cls, *args, **kw): self = cls.__new__(cls, *args, **kw) cls.__init__(self, *args, **kw) return self 

So, if you write a metaclass, overriding __call__ and suppressing the __init__ call in them, it will not be called at all:

 class Meta(type): def __call__(cls, *args, **kw): return cls.__new__(cls, *args, **kw) class NoInit(object): __metaclass__ = Meta def __init__(self): print "Hello!" NoInit() 

If you just want to avoid that subwords have __init__ instead of not calling it, you can make a much simpler metaclass that will simply raise an exception when creating an instance of the class:

 class Meta(type): def __new__(metacls, name, bases, dct): if "__init__" in dct: raise NameError("Classes in this hierarchy should not have an __init__ method") return type.__new__(metacls, name, bases, dct) 
+8
source

Source: https://habr.com/ru/post/1395349/


All Articles