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) # 9Code 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)) # TrueCode 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.


Comments

Leave a Reply

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