In this article, we will cover the object instantiation process used in python to create objects.
In this article you will learn:
- What is an object in python
- Object instantiation process in Python
- The
__new__
method - The
__call__
method - Callable in python
Python is an Object-oriented programming language. Everything in python is an object or instance, such as class, function, integer, float, etc. So each object has a class from which it is instantiated. We can use type(obj) function or __class__ property
to check the type of the object.
>>> type(a)
<class 'int'>
>>> a.__class__
<class 'int'>
In python, all classes inherits from the object
(built-in) class and this object class provides some common methods such as __new__, __init__, __doc__, __dir__ etc.
>>> dir(object)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
In python, Classes are themselves objects of class type
.
Note: This is different from the type
function. It is a metaclass from which all the classes are created.
>>> class Vehicle:
... pass
...
>>> veh = Vehicle()
>>> type(veh)
<class '__main__.Vehicle'>
>>> type(Vehicle)
<class 'type'>
Object instantiation process in Python:
- 👉Object Initialization in Python is a two-step process. Whenever a class is instantiated
__new__
and__init__
methods are called.__new__
method will be called to create a new object, as an instance of the desired class (object creation), and__init__
method will be called to initialize the object (object initialization).
👉
__new__
is a static method that makes sense since we are yet to create the instance.👉
__new__
method takes the class of which an instance was requested as its first argument(cls or class reference) and the additional arguments we pass the call to class. example: name, age, etc.
- 👉As per python official doc: Typical implementations create a new instance of the class by invoking the superclass’s
__new__()
method usingsuper().__new__(cls[, ...]) (same as object.__new__(cls, ...) since object is the parent class of all classes
with appropriate arguments and then modifying the newly-created instance as necessary before returning it.
class Person:
def __new__(cls, name, age):
print(f'Creating instance {cls.__name__} with args {name}, {age}')
instance = super().__new__(cls) # delegate to object.__new__
# don't forget to return the new instance!, otherwise
# init won't be called
return instance
def __init__(self, name, age):
print('Initializing instance...', name, age)
self.name = name
self.age = age
if __name__=='__main__':
p = Person('viscabar', 25)
if __name__=='__main__':
p = Person('viscabar', 25)
>>>
Creating instance Person with args viscabar, 25
Initializing instance... viscabar 25
Note: We must call the object class __new__
method inside the overridden __new__
method to create the object and allocate memory to the object.
Here inside __new__
method of Person class, we are calling object class __new__
class and this will create an instance of our desired class (Person class) and returns it.
🙄Till now, you must have thought about why to override the __new__
as you can directly do this using only __init__
. Basically, It allows us to tweak how the class is created and so use it to initialize the object or modify it as required before returning it as shown below:
class Person:
def __new__(cls, age):
print(f'Creating instance {cls.__name__} with args: {age}')
instance = super().__new__(cls) # delegate to object.__new__
instance.name = 'viscabar'
# don't forget to return the new instance!, otherwise
# init won't be called
return instance
def __init__(self, age):
print('Initializing instance...', age)
self.age = age
if __name__=='__main__':
p = Person(25)
print(p.name)
>>>
Creating instance Person with args: 25
Initializing instance... 25
viscabar
Here the __new__
method of the Person class modifies the obj returned from the __new__
method of the object class and adds the name property to it. Thus, all objects created using the Person class will have a name property with the name 'viscabar'.
But that's not the only thing that happens - the __init__
is also automatically called right after.
But only if the type returned by __new__
matches the type specified as the first argument of __new__
(cls or class reference or in our case Person type)😮.
class Person:
def __new__(cls, name):
print(f'Creating instance of {cls.__name__}... not really...')
instance = str(name)
return instance
def __init__(self, name):
print('Init called...')
self.name = name
if __name__=='__main__':
p = Person('viscabar')
print(p, type(p))
>>>
Creating instance of Person... not really...
viscabar <class 'str'>
As you can see the __init__
was not called and that makes sense since __new__
is not returning an instance of Person (currently it is returning an instance of str) so, it does not make sense to invoke the__init__
for Person, nor for the newly created instance.
All the methods that are started with a double underscore (dunder) and end with a double underscore are known as magic methods🔮. These are called implicitly by python as we don't need to call them explicitly.
__call__
method:
So I guess till now you must have thought about this:
How does python calls the __new__
and __init__
automatically?
In python classes are callable. and callable objects are objects that can be called.You can use callable(obj_reference)
function to check if the object is callable or not.
>>> callable(object)
True
>>> callable(Person)
True
>>> a=10
>>> callable(a)
False
In python, __call__
method is a magic method that is used to make the objects callable. In Python, class is also a callable object. All classes in python are of type class therefore the type class must have a __call__
method. Hence, when we call Person instance p(), in the background, Python calls the __call__
method of the type class.
class Person:
def __new__(cls, name):
print(f'Creating instance of {cls.__name__}.')
instance = super().__new__(cls)
return instance
def __init__(self, name):
print('Init called...')
self.name = name
def __call__(cls):
print('Inside __call__ method')
if __name__=='__main__':
p = Person('viscabar')
p()
>>>
Creating instance of Person.
Init called...
Inside __call__ method
as we can see that Person instance is now callable since we have defined the __call__
method.
Below is the definition of type class __call__
method. it is not exactly the same since it is originally written in C language but below is the version of python how it should look like :
def __call__(cls, *args, **kwargs):
p = cls.__new__(*args, **kwargs)
# After __new__ method returns the object, __init__ method will only be called if
# p is not None
# p is an instance of class Person
# __init__ method is defined on the instance p class
if p is not None and isinstance(p, cls) and hasattr(p, '__init__'):
# As __init__ is called on p, self will be equal to p in __init__ method
p.init(*args, **kwargs)
return p
Conclusion:
I hope this will clarify the object creation and instantiation processes in Python. We also explored some of the magic methods (__init__, __new__, __call__
) in python.😀😀😀