Let’s start with some background: when you create an object in Python, it goes through a few steps before being fully initialized. First, Python calls the `__new__()` method to create a new instance of your class. Then, it calls the `__init__()` method to initialize that newly created object’s attributes.
But here’s where things get interesting: you can override both methods! This is like having an odd couple living together in your codebase they don’t necessarily have to be best friends, but sometimes they need to work together for the greater good (or at least, until one of them moves out).
Let’s dig into this at each method and how they can be used separately or together.
__new__(cls[, …])
The `__new__()` method is called when an object is being created using the class syntax (i.e., `MyClass(arg1, arg2)`) or by calling the `object.__new__()` function directly. This method returns a new instance of your class before any initialization takes place.
Here’s what it looks like in action:
# The __new__() method is called when an object is being created using the class syntax (i.e., MyClass(arg1, arg2)) or by calling the object.__new__() function directly. This method returns a new instance of your class before any initialization takes place.
# Here's what it looks like in action:
class MyClass:
def __new__(cls): # defining the __new__() method, which is called when an object is being created
print("Creating a new object...")
return super().__new__(cls) # using the super() function to return a new instance of the class before any initialization takes place
my_obj = MyClass() # creating an instance of the MyClass class
print(my_obj.__dict__) # printing the dictionary of the instance, which is currently empty since no attributes have been initialized yet.
In this example, the `__new__()` method is overridden to print a message when a new object is being created. The `super().__new__(cls)` call ensures that Python still creates a new instance of your class using its parent’s constructor (if it has one).
__init__(self[, …])
The `__init__()` method is called after the `__new__()` method and allows you to initialize any attributes for your object. This method takes an optional list of arguments that correspond to the parameters passed when creating a new instance using the class syntax (i.e., `MyClass(arg1, arg2)`).
Here’s what it looks like in action:
# This is a class definition for MyClass
class MyClass:
# This is the constructor method for MyClass, which is called when creating a new instance of the class
def __init__(self, value):
# This line initializes the attribute "value" for the object, using the value passed in as an argument
self.value = value
# This line creates a new instance of MyClass with the value "hello" and assigns it to the variable my_obj
my_obj = MyClass("hello")
# This line prints out the dictionary of attributes for the object my_obj, which in this case only contains the "value" attribute with the value "hello"
print(my_obj.__dict__) # prints {'value': 'hello'}
In this example, the `__init__()` method is overridden to initialize a new attribute called `value`. The argument passed when creating an instance of `MyClass` (i.e., “hello”) is assigned to that attribute.
Now let’s see how these methods can work together:
# Creating a class called MyClass
class MyClass:
# Overriding the __init__() method to initialize a new attribute called value
def __init__(self, value):
# Assigning the argument passed when creating an instance of MyClass to the value attribute
self.value = value
# Overriding the __new__() method to create a new object with the given value
def __new__(cls, value):
# Printing a message to indicate the creation of a new object with the given value
print("Creating a new object with value:", value)
# Returning the new object using the super() function
return super().__new__(cls)
# Creating an instance of MyClass with the value "hello"
my_obj = MyClass("hello")
# Printing the dictionary of the instance, which should only contain the value attribute
print(my_obj.__dict__) # prints {'value': 'hello'}
In this example, both the `__new__()` and `__init__()` methods are overridden to work together. The `__new__()` method takes an argument called `value`, which is printed when a new object is being created. This value is then passed on to the parent’s constructor using the `super().__new__(cls)` call, and finally assigned to the `self.value` attribute in the `__init__()` method.
They may not always get along perfectly, but when used together they can create some pretty powerful objects.