Operator Overloading

operator overloading lets objects coded with classes intercept and respond to operations that work on built-in types: addition, slicing, printing, qualification, and so on.

Let’s move on to the third and final major difference between classes and modules: operator overloading. In simple terms, It’s mostly just an automatic dispatch mechanism —expressions and other built-in operations route control to implementations in classes. Here, too, there is nothing similar in modules: modules can implement function calls, but not the behavior of expressions.

Here is a quick rundown of the main ideas behind overloading operators:

  1. Methods named with double underscores (_X_) are special hooks: In Python classes we implement operator overloading by providing specially named methods to intercept operations. The Python language defines a fixed and unchangeable mapping from each of these operations to a specially named method.
  2. Such methods are called automatically when instances appear in built-in operations: For instance, if an instance object inherits an _add_ method, that method is called whenever the object appears in a + expression. The method’s return value becomes the result of the corresponding expression.
  3. Classes may override most built-in type operations: There are dozens of special operator overloading method names for intercepting and implementing nearly every operation available for built-in types. This includes expressions, but also basic operations like printing and object creation.
  4. There are no defaults for operator overloading methods, and none are required: If a class does not define or inherit an operator overloading method, it just means that the corresponding operation is not supported for the class’s instances. If there is no _add_, for example, + expressions raise exceptions.
  5. Operators allow classes to integrate with Python’s object model: By overloading type operations, the user-defined objects we implement with classes can act just like built-ins, and so provide consistency as well as compatibility with expected interfaces.

Operator overloading is an optional feature; it’s used primarily by people developing tools for other Python programmers, not by application developers. And, candidly, you probably shouldn’t use it just because it seems clever or “cool.” Unless a class needs to mimic built-in type interfaces, it should usually stick to simpler named methods. Why would an employee database application support expressions like * and +, for example? Named methods like giveRaise and promote would usually make more sense.

Because of this, we won’t go into details on every operator overloading method available in Python. Still, there is one operator overloading method you are likely to see in almost every realistic Python class: the __init__ method, which is known as the constructor method and is used to initialize objects’ state. You should pay special attention to this method, because __init__, along with the self argument, turns out to be a key requirement to reading and understanding most OOP code in Python.

Now lets create a new subclass:

On to another example. This time, we’ll define a subclass of the prior section’s secondclass(in chapter 2) that implements three specially named attributes that Python will call automatically:

  • __init__ is run when a new instance object is created: self is the new thirdclass object1.
  • __add__ is run when a thirdclass instance appears in a + expression.
  • __str__ is run when an object is printed.

Our new subclass also defines a normally named method called mul, which changes the instance object in place. Here’s the new subclass:

class thirdclass(secondclass):
    def __init__(self, value):
        self.data = value
    def __add__(self, other):
        return thirdclass(self.data + other)
    def __str__(self):
        return '[thirdclass: %s]'% self.data
    def mul(self, other):
        self.data *= other
a = thirdclass("abc") # __init__ called
a.display()    # Inherited method called

#Output- Current value = “abc”

print(a)     # __str__: returns display string

#Output- [thirdclass: abc]

b = a + 'xyz'    # __add__: makes a new instance
b.display()     # b has all ThirdClass methods

#Output- Current value = “abcxyz”

print(b)     # __str__: returns display string

#Output- [thirdclass: abcxyz]

a.mul(3)    # mul: changes instance in place
print(a)

#Output- [thirdclass: abcabcabc]

thirdclass “is a” secondclass(in chapter2), so its instances inherit the customized display method from secondclass of the preceding section. This time, though, thirdclass creation calls pass an argument (e.g., “abc”). This argument is passed to the value argument in the __init__ constructor and assigned to self.data there. The net effect is that thirdclass arranges to set the data attribute automatically at construction time, instead of requiring setdata calls after the fact.

That’s it for today we will proceed in the next chapter.

Previous||Next Chapter