There are many questions on SO asking why python doesn't always call__init__ after object creation.The answer, of course, is found in this excerpt from the documentation:
If
__new__()returns an instance ofcls, then the new instance’s__init__()method will be invoked like__init__(self[, ...]), whereselfis the new instance and the remaining arguments are the same as were passed to__new__().If
__new__()does not return an instance ofcls, then the new instance’s__init__()method will not be invoked.
What is the design reason for this?
- 1Why should
__init__always be invoked?Simeon Visser– Simeon Visser2015-01-18 00:42:48 +00:00CommentedJan 18, 2015 at 0:42 - I never said it should. I'm just wondering why it is how it is.DanielSank– DanielSank2015-01-18 00:46:22 +00:00CommentedJan 18, 2015 at 0:46
- 3My guess would be that, if
__new__doesn't return an instance ofcls, Python doesn't have a value forselfto pass to__init__.Simeon Visser– Simeon Visser2015-01-18 00:48:44 +00:00CommentedJan 18, 2015 at 0:48
4 Answers4
__init__ does act like a constructor. It needs an instance to do its job like setting attributes and so on. If__new__ doesn't return explicitly returns an instance, thenNone is returned by default.
Imagine that what will happen when__init__ gets aNone as an input and trying to set attributes? It will raise an exception called"AttributeError: 'NoneType' object has no attribute xxxxx".
So I think it's natural that not to invoke__init__ when__new__ returns None.
Comments
In Python 2, you can't actually call a regular method with the first argument being anything other than an instance of the class (or a subclass):
class Foo(object): def __init__(self): passFoo.__init__()# TypeError: unbound method __init__() must be called with Foo instance as first argument (got nothing instead)Foo.__init__(3)# TypeError: unbound method __init__() must be called with Foo instance as first argument (got int instance instead)So__init__ isn't called because it cannotpossibly do anything other than immediately raise an exception. Not trying to call it is strictly more useful (though I don't think I've ever seen code take advantage of this).
Python 3 has a slightly simpler method implementation, and this restriction is no longer in place, but the__new__ semantics are the same. It doesn't make a lot of sense to try to run a class'sinitializer on a foreign object, anyway.
For a more designy answer, rather than a "because it's this way" answer:
Overriding__new__ is already a weird thing to do. By default, it returns an uninitialized object, which is a concept that Python tries very hard to hide. If you override it, you're probably doing something like this:
class Foo(object): def __new__(cls, some_arg): if some_arg == 15: # 15 is a magic number for some reason! return Bar() else: return super(Foo, cls).__new__(cls, some_arg)Let's imagine a Python variant that unconditionally called__init__ on the return value. I immediately see a number of problems.
When you returnBar(), should Python callBar.__init__ (which has already been called in this case) orFoo.__init__ (which is for a completely different type and would break whatever guaranteesBar makes)?
The answer surely has to be thatBar.__init__ is called. Does that mean that you have to return an uninitializedBar instance, using the mouthfulreturn Bar.__new__(Bar) instead? Python very rarely requires you to call dunder methods outside of usingsuper, so this would be highly unusual.
Where wouldBar.__init__'s arguments come from? BothFoo.__new__ andFoo.__init__ are passed the same arguments — those passed totype.__call__, which is what handlesFoo(...). But if you explicitly callBar.__new__, there's nowhere to remember the arguments you wanted to pass toBar.__init__. You can't store them on the newBar object, because that's whatBar.__init__ is supposed to do! And if you just said it gets the same arguments that were passed toFoo, you severely limit what types can be returned from__new__.
Or, what if you wanted to return an object that already exists? Python has no way to indicate that an object is "already" initialized — since uninitialized objects are a transient and mostly-internal thing only of interest to__new__ — so you'd have no way to say not to call__init__ again.
The current approach is a little clumsy, but I don't think there's any better alternative.__new__ issupposed to create storage space for the new object, and returning a different type of object altogether is just a really weird thing to do; this is the least-surprising and most-useful way Python can handle it.
If this limitation is getting in your way, remember that the entire__new__ and__init__ dance is just the behavior oftype.__call__. You're perfectly free to define your own__call__ behavior on a metaclass, or just swap your class out for a factory function.
3 Comments
__init__?__init__ should be called — the one on the original type, or the one belonging to the type you returned from__new__? and why? if i were to hazard a guess, it's probably just that it's awkward and unusual to return anuninitialized object of a different type from__new__, whereas returning an uninitialized object of the usual type is the default behavior.__new__/__init__ dance is just the implementation oftype.__call__, which you are free to completely change in a metaclass if you so desire.This is probably not THE design reason, but a nifty consequence if this design decision is that a class does not HAVE to return an instance of itself:
class FactoryClass(): def __new__(klass, *args, **kwargs): if args or kwargs: return OtherClass(*args, **kwargs) return super().__new__(klass)This could be useful sometimes. Obviously if you're returning some other class object, you don't want the init method of the factory class to be called.
The real reason which I hope the above example illustrates, and the bottom line, is that any other design decision would be nonsensical. New is the constructor for Python class objects, not init. Init is no different from any other class method (other than it being auto magically called after the object has been constructed); whether or not any of them get called depends on what happens in the constructor. This is just a natural way to do things. Any other way would be broken.
7 Comments
__init__ on something other than an instance of the factory class would be broken, nobody has given a real life example of why I would want a classFoo with aFoo.__new__ method that returns something other than aFooand such that this other object is not sensibly initialized byFoo.__init__.If__new__ doesn't return an instance ofcls, then passing the return value tocls.__init__ could lead to very bad things.
In particular, Python doesn'trequire you to return an instance of the same type of class.
Take this contrived example:
In [1]: class SillyInt(int): ...: def __new__(cls): ...: return "hello" ...: In [2]: si = SillyInt()In [3]: siOut[3]: 'hello'So we've created a subclass ofint, but our__new__ method returned astr object.
It wouldn't make a great deal of sense to then pass thatstring to our inheritedint.__init__ method for instantiation.Even if, in the particular case in question, it 'works' (in the sense of not throwing any errors), we may have created an object in an inconsistent state.
Ok, so what about a more concrete (albeit still contrived) example?
Say we create two classes which set the same attribute as part of their initialisation (in the__init__ method):
In [1]: class Tree(): ...: def __init__(self): ...: self.desc = "Tree" ...: In [2]: class Branch(): ...: def __init__(self): ...: self.desc = "Branch" ...:If we now create a subclass which, in a custom__new__ method, wants to return a different type of object (which may sometimes make sense in practice, even if it doesn't here!):
In [3]: class SillyTree(Tree): ...: def __new__(cls): ...: return Branch() ...:When instantiating this subclass, we can see that we've got an object of the expected type, and that theBranch initialiser was the one that was called:
In [4]: sillyTree = SillyTree()In [5]: sillyTree.descOut[5]: 'Branch'What would have happened if Python had unconditionally called the inherited initialiser?
Well, we can actually test this by calling the initialiser directly.
What we end up with is an instance ofBranch which was initialised as aTree, leaving the object in a very unexpected state:
In [6]: SillyTree.__init__(sillyTree)In [7]: sillyTree.descOut[7]: 'Tree'In [8]: isinstance(sillyTree, Branch)Out[8]: TrueIn [9]: isinstance(sillyTree, Tree)Out[9]: False2 Comments
Foo such thatFoo.__new__ makes something other thanFoo instancesand such that these other instances are not sensibly initialized byFoo.__init__. I suppose one can imagine reasons for that, but having one real example recorded in the answer would really help.Explore related questions
See similar questions with these tags.


