Exploring the Instantiation Process
For more information on properties, you can check out Python’s property(): Add Managed Attributes to Your Classes.
00:01
Exploring the Instantiation Process. To explore how Python’s instantiation process works internally, consider the following example of a Point
class that implements custom versions of both the .__new__()
and .__init__()
special methods, for demonstration purposes.
00:20
This line defines the Point
class using the class
keyword followed by the class name.
00:29
This defines the .__new__()
special method, which takes the class as its first argument. Note that using cls
as the name of this argument is a strong convention in Python, just like using self
to name the current instance is.
00:43
The method also takes *args
and **kwargs
, which allow for passing an undefined number of initialization arguments to the underlying instance. This line prints a message when .__new__()
runs the object creation step.
01:00
This complex-looking line creates a new Point
instance by calling the parent class’s .__new__()
method with cls
as an argument. In this example, object
is the parent class, and the call to super()
gives you access to it.
01:14
Then the instance is returned. This instance will be the first argument to .__init__()
. Here you define .__init__()
, which is responsible for the initialization step.
01:26
This method takes a first argument called self
, which holds a reference to the current instance. The method also takes two additional arguments, x
and y
.
01:37
These arguments hold initial values for the instance attributes .x
and .y
. You need to pass suitable values for these arguments to the call to Point()
, as you’ll learn in a moment. These lines print a message when .__init__()
runs the object initialization step, and then initialize the .x
and .y
attributes, respectively. To do this, they use the provided input arguments x
and y
. Finally, these lines implement the .__repr__()
special method, which provides a proper string representation for the Point
class.
02:10
With Point
in place, you can uncover how the instantiation process works in practice. Save the code to a file called point.py
and start a new Python session from a command-line window in the same directory as the code is saved.
02:26 Then run the code seen on-screen.
02:34
Calling the Point()
class constructor creates, initializes, and returns a new instance of the class. This instance is then assigned to the variable point
.
02:45
In this example, the call to the constructor also lets you know the steps that Python internally runs to construct the instance. First, Python calls .__new__()
and then .__init__()
, resulting in a new and fully initialized instance of Point
, as you confirmed at the end of the example.
03:03 To continue learning about class instantiation in Python, you can try running both steps manually.
03:13
Here, you first call .__new__()
on the Point
class, passing the class itself as the first argument to the method. This call only runs the first step of the instantiation process, creating a new and empty object. Note that creating an instance this way bypasses the call to .__init__()
, and the object is not initialized.
03:34
This can be demonstrated by trying to access the .x
and .y
attributes, which generate errors. Once you have the new object, then you can initialize it by calling .__init__()
with an appropriate set of arguments.
03:52
After this, the Point
object is properly initialized, with all of its attributes set up. Note that this code is intended to be a demonstration of how the instantiation process works internally.
04:03
It’s not something that you would typically do in real code. A subtle and important detail to note about the .__new__()
special method is that it can also a return an instance of a class different from the class that implements the method itself. When that happens, Python doesn’t call .__init__()
in the current class, because there’s no way to unambiguously know how to initialize an object of a different class.
04:29
Next, you’ll see an example of this on-screen, where the .__new__()
method of the B
class returns an instance of the A
class.
05:11
Because B.__new__()
returns an instance of a different class, Python doesn’t run B.__init__()
. To confirm this behavior, save the code into a file called ab_classes.py
and then run the following code in an interactive Python session.
05:32
The call to the B()
class instructor runs B.__new__()
, which returns an instance of A
instead of B
. And this is why B.__init__()
never runs.
05:44
Note that b
doesn’t have a .b_value
attribute. In contrast, b
does have an .a_value
attribute with a value of 42
.
05:54
This instance can be used to check if b
is a member of a given class. Note that it is not a member of class B
, but it is a member of class A
.
06:11
Now that you know the steps that Python takes internally to create instances of a given class, you’re ready to dig a little deeper into other characteristics of the .__init__()
and .__new__()
special methods and the steps that they run. So in the next section, you’ll start that off by looking at .__init__()
.
Become a Member to join the conversation.