Python Constructor and Initialisation

Photo by Pablo Merchán Montes on Unsplash

Most object-oriented programming languages have the concept of a constructor, a method that creates and initialises the object when it is created.

For Python, the constructor is split into __init__ (for initialisation)and __new__ (for creation).

Let’s look at how the other languages deal with constructor first.

JavaScript

In JavaScript, the constructor method is a syntax sugar method of a class for creating and initialising an object of that class. And class is another syntax sugar for a type of function.

class Polygon {
constructor() {
this.name = ‘Polygon’;
}
}
const poly1 = new Polygon();console.log(poly1.name);
// expected output: “Polygon”

When the code new Polygon(...) is executed, the following things happen:

  • It first creates an empty new object {}
  • It creates __proto__ on poly1 and makes it point to Polygon.prototype so poly1.__proto__ === Polygon.prototype
  • It executes poly1.prototype.constructor (which is definition of function Polygon ) with the newly created empty object as its context (this), so the name property gets added to newly created object.
  • It returns newly created object

Python __new__ vs. __init__

object.__new__(cls[, ...])

Called to create a new instance of class cls. __new__() is a static method that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression . The return value of __new__() should be the new object instance (usually an instance of cls).

If __new__() is invoked during object construction and it returns an instance or subclass of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to the object constructor.

object.__init__(self[, ...])

Called after the instance has been created by __new__(), but before it is returned to the caller.

Because __new__() and __init__() work together in constructing objects (__new__() to create it, and __init__() to customize it), no non-None value may be returned by __init__(); doing so will cause a TypeError to be raised at runtime.

So per the documentation says, __new__ is for the creation of a new instance while __init__ is for the initialisation of a new instance returned from __new__.

Most time in Python, we won’t need to touch __new__ when e.g. we define a custom class, but we can define a custom __init__. So how can our custom __init__ call the default __new__?

class Polygon():
def __init__(self, area):
self.area = area
poly1 = Polygon(20)

That’s because the `Polygon.__new__ is able to access the default object.__new__ through the MRO order. So calling Polygon.__new__ is the same as calling object.__new__ if no other parent class is defined.

  • Once the Polygon.__new__ (aka. object.__new__) is found, it passes object.__new__(Polygon, *args, **kwargs) aka. Polygon.__new__(Polygon, area=20)
  • An instance of Polygon is created aka poly1 and the __init__ method is invoked on the instancepoly1 object.__new__(self, *args, **kwargs) aka. object.__new__(poly1, area=20)

What if it involves class inheritance? The result is very similar:

class Polygon():
def __init__(self, area):
self.area = area
class Square(Polygon):
def __init__(self, area):
super().__init__(area)
square1 = Square(10)print(Square.__mro__)
print(Square.__new__ is Polygon.__new__)
print(Square.__new__ is object.__new__)
====================>
(<class '__main__.Square'>, <class '__main__.Polygon'>, <class 'object'>)
True
True

So most of time we don’t need to touch __new__ . But sometimes we can use __new__() to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customise class creation.

Below is an example just to showcase you can bend the rule.

class Polygon:
def __new__(cls):
print (f"construct {cls}")
return object.__new__(cls)
poly1 = Polygon()===============>
construct <class '__main__.Polygon'>

The following is a more common example for subclass immutable object:

class ModularTuple(tuple):
def __new__(cls, tup, times=3):
tup = (int(x) * times for x in tup)
return super(ModularTuple, cls).__new__(cls, tup)
mt = ModularTuple((1,2,3))print(mt)
print(tuple.__new__ is ModularTuple.__new__)
======================>
(3, 6, 9)
True

I hope you get a basic understanding of __init__ and __new__ in Python and how the two interacts with each other.

Happy Reading!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store