Student Login

Overloaded overloading

A good question from one of y'all good readers:

"If Python doesn't support overloading, how does '+' work which can be either addition or concatenation, correct?"

This is a fun question, because the word "overloading" is used for two different things.

(You might say its definition is... overloaded.)

It's true that Python does not support method overloading. A class can have only one method of a given name. For example:

  1. import math
  2. class Point:
  3. def __init__(self, x, y):
  4. self.x, self.y = x, y
  5. def distance(self, other_point):
  6. 'Distance from this point to another point'
  7. return math.sqrt( (self.x-other_point.x)**2 + (self.y-other_point.y)**2 )
  8. def distance(self, x, y):
  9. 'Distance from this point to other x-y coordinates'
  10. return math.sqrt( (self.x-x)**2 + (self.y-y)**2 )

See the two distance() methods? In Python, only the second version is kept. If you call Point.distance() with one argument, you'll get an error.

We say Python does "single dispatch". Because it doesn't support method overloading - also called "multiple dispatch".

(What if you really need both distance() methods? See the P.S. below.)

Now... how does "+" work for both strings and numbers? Because Python DOES support operator overloading. And that's different from method overloading.

Python lets you overload its built-in operators, using its system of "magic method" hooks. For example:

  1. class Point:
  2. def __init__(self, x, y):
  3. self.x, self.y = x, y
  4. # ...
  5. def __add__(self, other):
  6. return Point(self.x + other.x, self.y + other.y)

This "__add__" method can be defined on any of your classes. When you do, it lets you add instances of them together:

  1. >>> p = Point(1,1) + Point(2,2)
  2. >>> print(p.x, p.y)
  3. 3 3

If you check, you'll find both float() and str() have their own __add__ methods. And they implement addition and string concatenation, respectively.

So that's the answer. Python supports one kind of overloading, but not the other.

P.S. Going back to the Point class - what would you do if you really need overloading for the distance() method?

Answer: You implement the method overloading yourself, by making distance() a dispatch method. Typically you do this with generic argument names, and checking types with isinstance(), or the presence/absence of certain arguments:

  1. import math
  2. class Point:
  3. def __init__(self, x, y):
  4. self.x, self.y = x, y
  5. def distance(self, first, second=None):
  6. if isinstance(first, Point):
  7. # distance to other Point
  8. assert second is None
  9. return self.distance_to_other(first)
  10. elif isinstance(first, tuple):
  11. # distance to (x, y) tuple
  12. assert second is None
  13. return self.distance_to_pair(first)
  14. else:
  15. # distance to coordinates
  16. assert second is not None
  17. return self.distance_to_coordinates(first, second)
  18. def distance_to_coordinates(self, x, y):
  19. 'Distance from this point to other x-y coordinates'
  20. return math.sqrt( (self.x-x)**2 + (self.y-y)**2 )
  21. def distance_to_other(self, other_point):
  22. 'Distance from this point to another point'
  23. return self.distance_to_coordinates(other_point.x, other_point.y)
  24. def distance_to_pair(self, pair):
  25. 'Distance from this point to (x, y) tuple'
  26. x, y = pair
  27. return self.distance_to_coordinates(x, y)

For Teams Bootcamp