Understanding__new__and__init__

__NEW__ AND __INIT__ IN OLD-STYLE VERSUS NEW-STYLE CLASSES


The purpose of this article is to discuss around __new__ and __init__ methods from Python. The __new__ and __init__ methods behave differently between themselves and between the old-style versus new-style python class definitions.

Understanding the difference between __new__ and __init__


The major difference between these two methods is that __new__ handles object creation and __init__ handles object initialization. During instantiation these two have slight differences in how they work, when defined differently.

A bit about classes in python


Classes in python are classified as new-style and old-style. Old-style classes are the ones that are prior to python 3 and defined without inheriting from base class 'object', which in turn is inherited from 'type' by default. Old-style class in python 2.x :

class A:  # -> inherits from 'type'pass

New-style class in python 2.x:

class A(object):  # -> clearly inherits from 'object'pass

In python 3 there aren't new or old styles of classes and they inherit directly from 'object' so there is no need to specify it as a base anymore.

The object base class brings methods/properties that are common to all new-style classes. Throughout the rest of the article we will examine the __new__ and __init__ methods in both cases to determine how they behave and how we can use them.

Treating different cases


Before diving into the actual implementations you need to know that __new__ accepts cls as it's first parameter and __init__ accepts self, because when calling __new__ you actually don't have an instance yet, therefore no self exists at that moment, whereas __init__ is called after __new__ and the instance is in place, so you can use self with it.

__new__ and __init__ for old style classes


Old-style classes don't actually have a __new__ method because for them __init__ is the constructor, so basically if we would have:

class A:    def __new__(cls):        print "A.__new__ is called"  # -> this is never called

A()

the body of __new__ will never be executed in this case because it is not the purpose for old-style classes. If we were to override __init__ instead:

class A:def __init__(self):        print "A.__init__ called"

A()

the output would be:

A.__init__ called

Now let's try returning something from __init__:

class A:def __init__(self):        return 29

A()

this would yield:

TypeError: __init__() should return None

so that means we can't actually control what to return when instantiating the old-style class.

__new__ and __init__ for new style classes

The new-style classes let the developer override both __new__ and __init__ and they have distinct purposes, __new__ (the constructor) is solely for creating the object and __init__ (the initializer) for initializing it. Let's see the order of their execution:

class A(object):  # -> don't forget the object specified as basedef __new__(cls):        print "A.__new__ called"        return super(A, cls).__new__(cls)    def __init__(self):        print "A.__init__ called"

A()

the output will be:

A.__new__ called
A.__init__ called

You may be wondering from where is __init__ or __new__ called and what I have for you is that __new__ is called automatically when calling the class name (when instantiating), whereas __init__ is called every time an instance of the class is returned by __new__ passing the returned instance to __init__ as the 'self' parameter, therefore even if you were to save the instance somewhere globally/statically and return it every time from __new__, then __init__ will be called every time you do just that. Knowing this it means that if we were to ommit calling super for __new__ then __init__ won't be executed. Let's see if that's the case:

class A(object):def __new__(cls):print "A.__new__ called"def __init__(self):print "A.__init__ called"  # -> is actually never calledprint A()

whose output is:

A.__new__ called
None

Obviously the instantiation is evaluated to None since we don't return anything from the constructor. Wondering what might happen if we were to return something from __new__?

class A(object):def __new__(cls):print "A.__new__ called"return 29print A()

guess what the output is:

A.__new__ called
29

Let's see what happens when we try to return from __init__:

class A(object):def __init__(self):print "A.__init__ called"return 33  # -> TypeError: __init__ should return None

A()

which yields:

TypeError: __init__ should return None

This is mainly because the handler that calls __init__ raises the TypeError exception and it wouldn't even make sense to return anything from __init__ since it's purpose is just to alter the fresh state of the newly created instance.

It seems the new-style classes have much to offer in terms of flexibility, allowing us to do any PRE/POST operations at both creation and initialization levels and leaves us control over what we return at instantiation time. Considering that, let's try and return an instance of a different class. First we define our alternative class:

class Sample(object):def __str__(self):return "SAMPLE"

And then we define our __new__ overriding class:

class A(object):def __new__(cls):return Sample()

which could also be written as:

class A(object):    def __new__(cls):        return super(A, cls).__new__(Sample)

and then call:

print A()

that would output:

SAMPLE

References

https://docs.python.org/2/library/functions.html#object http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html#the-object-within https://wiki.python.org/moin/NewClassVsClassicClass

31 views0 comments

Recent Posts

See All

UNDERSTANDING WHAT CLASSES ARE, WHEN TO USE THEM, AND HOW THEY CAN BE USEFUL The class is a fundamental building block in Python. It is the underpinning for not only many popular programs and librarie

SPYHCE WAS THE PLEASED HOST OF ROPYTHON CLUJ MEETUP Cluj Napoca, June 11, 2015 , On the second week of June, Spyhce was the pleased host of RoPython Cluj Meetup, having on the agenda two fascinating s