Python __new__

Created with Sketch.

Python __new__

 

Summary: in this tutorial, you’ll learn about the Python __new__ method and understand how Python uses it to create a new object.

Introduction to the Python __new__ method

When you create an instance of a class, Python first calls the __new__() method to create the object and then calls the __init__() method to initialize the object’s attributes.

The __new__() is a static method of the object class. It has the following signature:

object.__new__(class, *args, **kwargs)

Code language: Python (python)

The first argument of the __new__ method is the class of the new object that you want to create.

The *args and **kwargs parameters must match the parameters of the __init__() of the class. However, the __new__() method does use them.

The __new__() method should return a new object of the class. But it doesn’t have to.

When you define a new class, that class implicitly inherits from the object class. It means that you can override the __new__ static method and do something before and after creating a new instance of the class.

To create the object of a class, you call the super().__new__() method.

Technically, you can call the object.__new__() method to create an object manually. However, you need to call the __init__() yourself manually after. Python will not call the __init__() method automatically if you explicitly create a new object using the object.__new__() method.

Python __new__ method example

The following defines the Person class with the name attribute and create a new instance of the Person class:

class Person:
def __init__(self, name):
self.name = name

person = Person('John')

Code language: Python (python)

In Python, a class is callable. When you call the class to create a new object:

person = Person('John')

Code language: Python (python)

Python will call the __new__() and __init__() methods. It’s equivalent to the following method calls:

person = object.__new__(Person, 'John')
person.__init__('John')

Code language: Python (python)

The following shows the contents of the __dict__ of the person object after calling the __new__() and __init__() methods:

person = object.__new__(Person, 'John')
print(person.__dict__)

person.__init__('John')
print(person.__dict__)

Code language: Python (python)

Output:

{}
{'name': 'John'}

Code language: Python (python)

As you can see clearly from the output, after calling the __new__() method, the person.__dict__ is empty. And after calling the __init__() method, the person.__dict__ contains the name attribute with the value ‘John'.

The following illustrates the sequence which Python calls the __new__ and __init__ method when you create a new object by calling the class:

class Person:
def __new__(cls, name):
print(f'Creating a new {cls.__name__} object...')
obj = object.__new__(cls)
return obj

def __init__(self, name):
print(f'Initializing the person object...')
self.name = name

person = Person('John')

Code language: Python (python)

Output:

Creating a new Person object...
Initializing the person object...

Code language: Python (python)

In this example, Python calls the __new__ method and the __init__ method after that.

When using the __new__ method

The following example defines the SquareNumber class that inherits from the built-in int type:

class SquareNumber(int):
def __new__(cls, value):
return super().__new__(cls, value ** 2)

x = SquareNumber(3)
print(x) # 9

Code language: Python (python)

In this example, the __new__() method of the SquareNumber class accepts an integer and returns the square number. x is an instance of the SquareNumber class and also an instance of the int built-in type:

print(isinstance(x, int)) # True

Code language: Python (python)

Note that you cannot do this by using the __init__() method because the __init__() method of the built-in int takes no argument. The following code will result in an error:

class SquareNumber(int):
def __init__(self, value):
super().__init__(value ** 2)

x = SquareNumber(3)

Code language: Python (python)

Error:

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

Code language: Python (python)

In practice, you use the __new__() method when you want to tweak the object at the instantiated time.

For example, the following defines the Person class and uses the __new__method to inject the full_name attribute to the Person’s object:

class Person:
def __new__(cls, first_name, last_name):
# create a new object
obj = super().__new__(cls)

# initialize attributes
obj.first_name = first_name
obj.last_name = last_name

# inject new attribute
obj.full_name = f'{first_name} {last_name}'
return obj

person = Person('John', 'Doe')
print(person.full_name)

print(person.__dict__)

Code language: Python (python)

Output:

John Doe
{'first_name': 'John', 'last_name': 'Doe', 'full_name': 'John Doe'}

Code language: Python (python)

Typically, when you override the __new__() method, you don’t need to define the __init__() method because everything you can do in the __init__() method, you can do it in the __new__() method.

Summary

  • The __new__() is a static method of the object class.
  • When you create a new object by calling the class, Python calls the __new__() method to create the object first and then calls the __init__() method to initialize the object’s attributes.
  • Override the __new__() method if you want to tweak the object at creation time.

Leave a Reply

Your email address will not be published. Required fields are marked *