A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://realpython.com/python-class-constructor/ below:

Control Your Object Instantiation – Real Python

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Using Python Class Constructors

Creating a class constructor in Python involves understanding the instantiation process, which consists of two steps: instance creation and instance initialization. You start this process by calling the class like a function, which triggers the .__new__() method to create an instance and the .__init__() method to initialize it. Mastering these methods allows you to customize how Python constructs and initializes objects of your classes.

By the end of this tutorial, you’ll understand that:

To better understand the examples and concepts in this tutorial, you should be familiar with object-oriented programming and special methods in Python.

Free Bonus: Click here to get access to a free Python OOP Cheat Sheet that points you to the best tutorials, videos, and books to learn more about Object-Oriented Programming with Python.

Take the Quiz: Test your knowledge with our interactive “Python Class Constructors: Control Your Object Instantiation” quiz. You’ll receive a score upon completion to help you track your learning progress:

Python’s Class Constructors and the Instantiation Process

Like many other programming languages, Python supports object-oriented programming. At the heart of Python’s object-oriented capabilities, you’ll find the class keyword, which allows you to define custom classes that can have attributes for storing data and methods for providing behaviors.

Once you have a class to work with, then you can start creating new instances or objects of that class, which is an efficient way to reuse functionality in your code.

Creating and initializing objects of a given class is a fundamental step in object-oriented programming. This step is often referred to as object construction or instantiation. The tool responsible for running this instantiation process is commonly known as a class constructor.

Getting to Know Python’s Class Constructors

In Python, to construct an object of a given class, you just need to call the class with appropriate arguments, as you would call any function:

In this example, you define SomeClass using the class keyword. This class is currently empty because it doesn’t have attributes or methods. Instead, the class’s body only contains a pass statement as a placeholder statement that does nothing.

Then you create a new instance of SomeClass by calling the class with a pair of parentheses. In this example, you don’t need to pass any argument in the call because your class doesn’t take arguments yet.

In Python, when you call a class as you did in the above example, you’re calling the class constructor, which creates, initializes, and returns a new object by triggering Python’s internal instantiation process.

A final point to note is that calling a class isn’t the same as calling an instance of a class. These are two different and unrelated topics. To make a class’s instance callable, you need to implement a .__call__() special method, which has nothing to do with Python’s instantiation process.

Understanding Python’s Instantiation Process

You trigger Python’s instantiation process whenever you call a Python class to create a new instance. This process runs through two separate steps, which you can describe as follows:

  1. Create a new instance of the target class
  2. Initialize the new instance with an appropriate initial state

To run the first step, Python classes have a special method called .__new__(), which is responsible for creating and returning a new empty object. Then another special method, .__init__(), takes the resulting object, along with the class constructor’s arguments.

The .__init__() method takes the new object as its first argument, self. Then it sets any required instance attribute to a valid state using the arguments that the class constructor passed to it.

In short, Python’s instantiation process starts with a call to the class constructor, which triggers the instance creator, .__new__(), to create a new empty object. The process continues with the instance initializer, .__init__(), which takes the constructor’s arguments to initialize the newly created object.

To explore how Python’s instantiation process works internally, consider the following example of a Point class that implements a custom version of both methods, .__new__() and .__init__(), for demonstration purposes:

Here’s a breakdown of what this code does:

With Point in place, you can uncover how the instantiation process works in practice. Save your code to a file called point.py and start your Python interpreter in a command-line window. Then run the following code:

Calling the Point() class constructor creates, initializes, and returns a new instance of the class. This instance is then assigned to the point variable.

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.

To continue learning about class instantiation in Python, you can try running both steps manually:

In this example, you first call .__new__() on your 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__().

Note: The code snippet above is intended to be a demonstrative example of how the instantiation process works internally. It’s not something that you would typically do in real code.

Once you have the new object, then you can initialize it by calling .__init__() with an appropriate set of arguments. After this call, your Point object is properly initialized, with all its attributes set up.

A subtle and important detail to note about .__new__() is that it can also 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.

Consider the following example, in which the .__new__() method of the B class returns an instance of the A class:

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:

The call to the B() class constructor runs B.__new__(), which returns an instance of A instead of B. That’s why B.__init__() never runs. Note that b doesn’t have a .b_value attribute. In contrast, b does have an .a_value attribute with a value of 42.

Now that you know the steps that Python internally takes to create instances of a given class, you’re ready to dig a little deeper into other characteristics of .__init__(), .__new__(), and the steps that they run.

Object Initialization With .__init__()

In Python, the .__init__() method is probably the most common special method that you’ll ever override in your custom classes. Almost all your classes will need a custom implementation of .__init__(). Overriding this method will 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.

Providing Custom Object Initializers

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, say you’re writing a Rectangle class that requires .width and .height attributes. In that case, you can do something like this:

As you learned before, .__init__() runs the second step of the object instantiation process in Python. Its first argument, self, holds the new instance that results from calling .__new__(). The rest of the arguments to .__init__() are normally used to initialize instance attributes. In the above example, you initialized the rectangle’s .width and .height using the width and height arguments to .__init__().

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 different from None, or you’ll get a TypeError exception:

In this example, the .__init__() method attempts to return an integer number, which ends up raising a TypeError exception at run time.

The error message in the above 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 just return None implicitly in Python.

With the above implementation of .__init__(), you ensure that .width and .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 instance attributes. For example, if your users will use Rectangle directly, then you might want to validate the supplied width and 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 width and height arguments are positive numbers before initializing the corresponding .width and .height attributes. If either validation fails, then you get a ValueError.

Note: A more Pythonic technique to tackle attribute validation is to turn attributes into properties. To learn more about properties, check out Python’s property(): Add Managed Attributes to Your Classes.

Now say that you’re using inheritance to create a custom class hierarchy and reuse some functionality in your code. If your subclasses provide a .__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 like in the following example:

The first line in the .__init__() method of Employee calls super().__init__() with name and birth_date as arguments. This call ensures the initialization of .name and .birth_date in the parent class, Person. This technique allows you to extend the base class with new attributes and functionality.

To wrap up this section, 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 explicit .__init__() method in your classes.

Building Flexible Object Initializers

You can make your objects’ initialization step flexible and versatile by tweaking the .__init__() 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 Greeter class:

In this example, .__init__() takes a regular argument called name. It also takes an optional argument called formal, which defaults to False. Because formal has a default value, you can construct objects by 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 an informal greeting when you call .greet(). Otherwise, you’ll get a more formal greeting.

To try Greeter out, go ahead and save the code into a greet.py file. Then open an interactive session in your working directory and run the following code:

In the first example, you create an informal_greeter object by passing a value to the name argument and relying on the default value of formal. You get an informal greeting on your screen when you call .greet() on the informal_greeter object.

In the second example, you use a name and a formal argument to instantiate Greeter. Because formal is 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.

Okay! Now that you know the basics of .__init__() and the object initialization step, it’s time to change gears and start diving deeper into .__new__() and the object creation step.

Object Creation With .__new__()

When writing Python classes, you typically don’t need to provide your own implementation of the .__new__() special method. Most of the time, the base implementation from the built-in object class is sufficient to build an empty object of your current class.

However, there are a few interesting use cases for this method. For example, you can use .__new__() to create subclasses of immutable types, such as int, float, tuple, and str.

In the following sections, you’ll learn how to write custom implementations of .__new__() in your classes. To do this, you’ll code a few examples that’ll give you an idea of when you might need to override this method.

Providing Custom Object Creators

Typically, you’ll write a custom implementation of .__new__() only when you need to control the creation of a new instance at a low level. Now, if you need a custom implementation of this method, then you should follow a few steps:

  1. Create a new instance by calling super().__new__() with appropriate arguments.
  2. Customize the new instance according to your specific needs.
  3. Return the new instance to continue the instantiation process.

With these three succinct steps, you’ll be able to customize the instance creation step in the Python instantiation process. Here’s an example of how you can translate these steps into Python code:

This example provides a sort of template implementation of .__new__(). As usual, .__new__() takes the current class as an argument that’s typically called cls.

Note that you’re using *args and **kwargs to make the method more flexible and maintainable by accepting any number of arguments. You should always define .__new__() with *args and **kwargs, unless you have a good reason to follow a different pattern.

In the first line of .__new__(), you call the parent class’s .__new__() method to create a new instance and allocate memory for it. To access the parent class’s .__new__() method, you use the super() function. This chain of calls takes you up to object.__new__(), which is the base implementation of .__new__() for all Python classes.

Note: The built-in object class is the default base class of all Python classes.

The next step is to customize your newly created instance. You can do whatever you need to do to customize the instance at hand. Finally, in the third step, you need to return the new instance to continue the instantiation process with the initialization step.

It’s important to note that object.__new__() itself only accepts a single argument, the class to instantiate. If you call object.__new__() with more arguments, then you get a TypeError:

In this example, you hand over *args and **kwargs as additional arguments in the call to super().__new__(). The underlying object.__new__() accepts only the class as an argument, so you get a TypeError when you instantiate the class.

However, object.__new__() still accepts and passes over extra arguments to .__init__() if your class doesn’t override .__new__(), as in the following variation of SomeClass:

In this implementation of SomeClass, you don’t override .__new__(). The object creation is then delegated to object.__new__(), which now accepts value and passes it over to SomeClass.__init__() to finalize the instantiation. Now you can create new and fully initialized instances of SomeClass, just like some_obj in the example.

Cool! Now that you know the basics of writing your own implementations of .__new__(), you’re ready to dive into a few practical examples that feature some of the most common use cases of this method in Python programming.

Subclassing Immutable Built-in Types

To kick things off, you’ll start with a use case of .__new__() that consists of subclassing an immutable built-in type. As an example, say you need to write a Distance class as a subclass of Python’s float type. Your class will have an additional attribute to store the unit that’s used to measure the distance.

Here’s a first approach to this problem, using the .__init__() method:

When you subclass an immutable built-in data type, you get an error. Part of the problem is that the value is set during creation, and it’s too late to change it during initialization. Additionally, float.__new__() is called under the hood, and it doesn’t deal with extra arguments in the same way as object.__new__(). This is what raises the error in your example.

To work around this issue, you can initialize the object at creation time with .__new__() instead of overriding .__init__(). Here’s how you can do this in practice:

In this example, .__new__() runs the three steps that you learned in the previous section. First, the method creates a new instance of the current class, cls, by calling super().__new__(). This time, the call rolls back to float.__new__(), which creates a new instance and initializes it using value as an argument. Then the method customizes the new instance by adding a .unit attribute to it. Finally, the new instance gets returned.

Note: The Distance class in the example above doesn’t provide a proper unit conversion mechanism. This means that something like Distance(10, "km") + Distance(20, "miles") won’t attempt at converting units before adding the values. If you’re interested in converting units, then check out the Pint project on PyPI.

That’s it! Now your Distance class works as expected, allowing you to use an instance attribute for storing the unit in which you’re measuring the distance. Unlike the floating-point value stored in a given instance of Distance, the .unit attribute is mutable, so you can change its value any time you like. Finally, note how a call to the dir() function reveals that your class inherits features and methods from float.

Returning Instances of a Different Class

Returning an object of a different class is a requirement that can raise the need for a custom implementation of .__new__(). However, you should be careful because in this case, Python skips the initialization step entirely. So, you’ll have the responsibility of taking the newly created object into a valid state before using it in your code.

Check out the following example, in which the Pet class uses .__new__() to return instances of randomly selected classes:

In this example, Pet provides a .__new__() method that creates a new instance by randomly selecting a class from a list of existing classes.

Here’s how you can use this Pet class as a factory of pet objects:

Every time you instantiate Pet, you get a random object from a different class. This result is possible because there’s no restriction on the object that .__new__() can return. Using .__new__() in such a way transforms a class into a flexible and powerful factory of objects, not limited to instances of itself.

Finally, note how the .__init__() method of Pet never runs. That’s because Pet.__new__() always returns objects of a different class rather than of Pet itself.

Allowing Only a Single Instance in Your Classes

Sometimes you need to implement a class that allows the creation of a single instance only. This type of class is commonly known as a singleton class. In this situation, the .__new__() method comes in handy because it can help you restrict the number of instances that a given class can have.

Note: Most experienced Python developers would argue that you don’t need to implement the singleton design pattern in Python unless you already have a working class and need to add the pattern’s functionality on top of it.

The rest of the time, you can use a module-level constant to get the same singleton functionality without having to write a relatively complex class.

Here’s an example of coding a Singleton class with a .__new__() method that allows the creation of only one instance at a time. To do this, .__new__() checks the existence of previous instances cached on a class attribute:

The Singleton class in this example has a class attribute called ._instance that defaults to None and works as a cache. The .__new__() method checks if no previous instance exists by testing the condition cls._instance is None.

Note: In the example above, Singleton doesn’t provide an implementation of .__init__(). If you ever need a class like this with a .__init__() method, then keep in mind that this method will run every time you call the Singleton() constructor. This behavior can cause weird initialization effects and bugs.

If this condition is true, then the if code block creates a new instance of Singleton and stores it to cls._instance. Finally, the method returns the new or the existing instance to the caller.

Then you instantiate Singleton twice to try to construct two different objects, first and second. If you compare the identity of these objects with the is operator, then you’ll note that both objects are the same object. The names first and second just hold references to the same Singleton object.

Partially Emulating collections.namedtuple

As a final example of how to take advantage of .__new__() in your code, you can push your Python skills and write a factory function that partially emulates collections.namedtuple(). The namedtuple() function allows you to create subclasses of tuple with the additional feature of having named fields for accessing the items in the tuple.

The code below implements a named_tuple_factory() function that partially emulates this functionality by overriding the .__new__() method of a nested class called NamedTuple:

Here’s how this factory function works line by line:

To try your named_tuple_factory() out, fire up an interactive session in the directory containing the named_tuple.py file and run the following code:

In this code snippet, you create a new Point class by calling named_tuple_factory(). The first argument in this call represents the name that the resulting class object will use. The second and third arguments are the named fields available in the resulting class.

Then you create a Point object by calling the class constructor with appropriate values for the .x and .y fields. To access the value of each named field, you can use the dot notation. You can also use indices to retrieve the values because your class is a tuple subclass.

Because tuples are immutable data types in Python, you can’t assign new values to the point’s coordinates in place. If you try to do that, then you get an AttributeError.

Finally, calling dir() with your point instance as an argument reveals that your object inherits all the attributes and methods that regular tuples have in Python.

Conclusion

Now you know how Python class constructors allow you to instantiate classes, so you can create concrete and ready-to-use objects in your code. In Python, class constructors internally trigger the instantiation or construction process, which goes through instance creation and instance initialization. These steps are driven by the .__new__() and .__init__() special methods.

By learning about Python’s class constructors, the instantiation process, and the .__new__() and .__init__() methods, you can now manage how your custom classes construct new instances.

In this tutorial, you learned:

Now you’re ready to take advantage of this knowledge to fine-tune your class constructors and take full control over instance creation and initialization in your object-oriented programming adventure with Python.

Free Bonus: Click here to get access to a free Python OOP Cheat Sheet that points you to the best tutorials, videos, and books to learn more about Object-Oriented Programming with Python.

Frequently Asked Questions

Now that you have some experience with Python class constructors, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

A class constructor in Python is a mechanism that creates and initializes a new instance of a class. It involves calling the class itself like a function, which triggers the instantiation process, including the execution of the .__new__() and .__init__() methods.

You can customize object initialization in Python by overriding the .__init__() method in your class. This method allows you to set up the initial state of an object by assigning values to instance attributes using the arguments passed to the class constructor.

The .__new__() method is responsible for creating a new instance of a class, while the .__init__() method initializes the newly created instance. .__new__() returns the new instance, and .__init__() sets up the initial state of the instance.

Common use cases for overriding .__new__() include creating instances of immutable built-in types like int or tuple, controlling the instantiation of singleton classes, and returning instances of a different class to customize object creation.

If you don’t override .__new__() or .__init__() in your class, Python will use the default implementations from the built-in object class. They’re typically sufficient for most use cases unless you need custom behavior during object creation or initialization.

Take the Quiz: Test your knowledge with our interactive “Python Class Constructors: Control Your Object Instantiation” quiz. You’ll receive a score upon completion to help you track your learning progress:

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Using Python Class Constructors


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4