__init__ is the initialiser

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Feb 3 01:33:10 CET 2014


On Mon, 03 Feb 2014 12:38:00 +1300, Gregory Ewing wrote:

> Steven D'Aprano wrote:
>> (In hindsight, it was probably a mistake for Python to define two
>> create- an-object methods, although I expect it was deemed necessary
>> for historical reasons.
> 
> I'm not sure that all of the reasons are historical. Languages that have
> a single creation/initialisation method also usually have a mechanism
> for automatically calling a base version of the method if you don't do
> that explicitly, and they typically do it by statically analysing the
> source. That's not so easy in a dynamic language.

Just because statically-typed languages do it at compile-time doesn't 
mean Python couldn't do it at run-time. All the information is readily 
available, so Python could do something like this:

# --- Untested ---
# Automatically call each __new__ constructor method, starting from
# the most fundamental (object) and ending with the current class.
stack = []
for c in cls.__mro__:
    if hasattr(c, '__new__'):
        stack.append(c.__new__)
while stack:
    stack.pop()(*args)


Note that this design is sub-optimal: the constructor methods don't 
receive the newly-created instance as an argument, which makes it hard to 
do initialisation, and makes the whole exercise rather pointless. But 
with a slight change of semantics, we can make this work rather sensibly. 
Change the signature of __new__ to:

def __new__(cls, self=None, *args, **kwargs)

and the last two lines to:

instance = None
while stack:
    instance = stack.pop()(instance, *args)


Is this a good design? Possibly not. But it's possible, and not terribly 
hard. Dynamism is no barrier to automatically calling constructors.

What I meant by backwards compatibility is that prior to the introduction 
of new-style classes, you couldn't override __new__, only __init__. So if 
you had a classic class, you'd have to receive the instance:

class Classic:
    def __init__(self, *args):
        ...


but for new-style classes, you'd receive the class:

class Newstyle(object):
    def __init__(cls, *args):
        ...


which is confusing and awkward, and would make it annoying to migrate 
from classic classes to new-style classes. So from the backwards-
compatibility perspective, __init__ has to receive the instance (self) as 
first argument. So the simplest way to satisfy that requirement, and 
still allow the class to define a constructor method that receives the 
class and constructs the instance, is to define a second special method. 
Which is what was done.


> If Python only had __new__, everyone who overrode it would have to start
> with an explicit call to the base class's __new__, adding a lot of
> boilerplate and forcing people to learn how to make base method calls
> much sooner than they would otherwise need to.

There is that as well.



-- 
Steven



More information about the Python-list mailing list