Initializing Objects With .__init__()
Object Initialization With
.__init__(). In Python, the
.__init__() method is probably the most common special method you’ll override in your custom classes.
Almost all your classes will need a custom implementation of
.__init__() to allow you to initialize your objects properly. The purpose of this initialization step is to leave your new objects in a valid state so that you can start using them right away in your code. In this section, you’ll learn the basics of writing your own
.__init__() methods and how they can help you customize your classes.
The most bare-bones implementation of
.__init__() that you can write will just take care of assigning input arguments to matching instance attributes.
For example, let’s say you’re writing a
Rectangle class that requires
.height attributes. In that case, you’d create codes similar to what’s seen on-screen.
As you’ve already learned,
.__init__() runs the second step of the object instantiation process in Python. Its first argument,
self, holds the new instance that results from calling
The rest of the arguments to
.__init__() are normally used to initialize instance attributes. Here, you initialize the rectangle’s
.height attributes using the
height arguments to
It’s important to note that, without counting
self, the arguments to
.__init__() are the same ones that you passed in the call to the class constructor. So, in a way, the
.__init__() signature defines the signature of the class constructor.
Additionally, keep in mind that
.__init__() must not explicitly return anything other than
None, or you’ll get a
.__init__() method attempts to return an integer, which ends up raising a
TypeError exception at runtime.
The error message in this example says that
.__init__() should return
None. However, you don’t need to return
None explicitly, because methods and functions without an explicit
return statement return
None implicitly in Python.
With this implementation of
.__init__(), you ensure that
.height get initialized to a valid state when you call the class constructor with appropriate arguments.
That way, your rectangles will be ready for use right after the construction process finishes. In
.__init__(), you can also run any transformation over the input arguments to properly initialize the instant attributes. For example, if your users will use
Rectangle directly, then you might want to validate the supplied
height and make sure that they’re correct before initializing the corresponding attributes.
In this updated implementation of
.__init__(), you make sure that the input
height arguments are positive numbers before initializing the corresponding
If either validation fails, then you raise a
ValueError, as seen on-screen.
03:27 A more Pythonic technique to tackle attribute validation is to turn attributes into properties. To learn more about properties, check out this Real Python course.
Now let’s say that you are using inheritance to create a custom class hierarchy and reuse some functionality in your code. If your subclasses provide an
.__init__() method, then this method must explicitly call the base class’s
.__init__() method with appropriate arguments to ensure the correct initialization of instances. To do this, you should use the built-in
super() function, as on-screen.
The first line in
.__init__() method calls
super().__init__() with a
birth_date as arguments.
This call ensures the initialization of
birth_date in the parent class,
Person. This technique allows you to extend the base class with new attributes and functionality. Here, the
.position attribute is used only by the
Employee class and is part of the
Employee() initialization function.
This is confirmed by viewing the attributes of the
john object, as seen on-screen.
You should know that the base implementation of
.__init__() comes from the built-in
object class. This implementation is automatically called when you don’t provide an
.__init__() method in your classes.
You can make your objects’ initialization step flexible and versatile by tweaking the
.__init__() special method. To this end, one of the most popular techniques is to use optional arguments.
This technique allows you to write classes in which the constructor accepts different sets of input arguments at instantiation time. Which arguments to use at a given time will depend on your specific needs and context. As a quick example, check out the following
.__init__() takes a regular argument called
name. It also takes an optional argument called
formal, which defaults to
formal has a default value, you can construct objects relying on this value or by providing your own.
The class’s final behavior will depend on the value of
formal. If this argument is false, then you’ll get informal greeting when you call
.greet(). Otherwise, you’ll get a more formal greeting.
To try the
Greeter class out, save the code into a file called
greet.py then open a Python session in the working directory and run the following code.
Here, you create an
informal_greeter object by passing a value to the
name argument and on the default value of
You get an informal greeting on your screen when you call
.greet() on the
informal_greeter object. In this example, you use a
name and a
formal argument to instantiate
True, the result of calling
.greet() is a formal greeting. Even though this is a toy example, it showcases how default argument values are a powerful Python feature that you can use to write flexible initializers for your classes.
These initializers will allow you to instantiate your classes using different sets of arguments depending on your needs. Now that you know the basics of the
.__init__() special method and the object initialization step, it’s time to change gears and start diving deeper into
.__new__() and the object creation step.
Become a Member to join the conversation.