For more information on properties, you can check out Python’s property(): Add Managed Attributes to Your Classes.
Exploring the Instantiation Process
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
.__init__() special methods, for demonstration purposes.
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.
The method also takes
**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.
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.
These arguments hold initial values for the instance attributes
.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
.y attributes, respectively. To do this, they use the provided input arguments
y. Finally, these lines implement the
.__repr__() special method, which provides a proper string representation for the
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.
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.
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.
This can be demonstrated by trying to access the
.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.
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.
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.
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
.__new__() special methods and the steps that they run. So in the next section, you’ll start that off by looking at
Become a Member to join the conversation.