6.1 Magic Methods
Operator overloading in Python allows you to define or change the behavior of built-in operators (like +
, -
, *
, /
) for custom classes. This is done using special methods, which are also called magic methods.
For example, in your class you can overload comparison operators:
Operator | Method without underscores | Method Signature |
---|---|---|
== | eq() |
__eq__(self, other) |
!= | ne() |
__ne__(self, other) |
< | lt() |
__lt__(self, other) |
<= | le() |
__le__(self, other) |
> | gt() |
__gt__(self, other) |
>= | ge() |
__ge__(self, other) |
Suppose you wrote your own class and you want the objects of your class to be compared exactly the way you need. You just need to implement the «__eq__»
method in your class, and Python will call it every time objects of your class are compared in the code.
Example:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Usage
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2) # Outputs: True
print(v1 == v3) # Outputs: False
Every time you compare two of your objects, Python checks if they have an implemented function «__eq__»
. If there is, it calls it. If not, it will just compare the references to the objects.
In fact, the example above is written (just need to add a check for the method's existence):
# Usage
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2)) # Outputs: True
print(v1.__eq__(v3)) # Outputs: False
6.2 List of All Operators
There are a total of 6 groups of operators available for overloading.
Arithmetic Operators:
Operator | Method without underscores | Method Signature |
---|---|---|
+ | add |
__add__(self, other) |
- | sub |
__sub__(self, other) |
* | mul |
__mul__(self, other) |
/ | truediv |
__truediv__(self, other) |
// | floordiv |
__floordiv__(self, other) |
% | mod |
__mod__(self, other) |
** | pow |
__pow__(self, other) |
Comparison Operators:
Operator | Method without underscores | Method Signature |
---|---|---|
== | eq |
__eq__(self, other) |
!= | ne |
__ne__(self, other) |
< | lt |
__lt__(self, other) |
<= | le |
__le__(self, other) |
> | gt |
__gt__(self, other) |
>= | ge |
__ge__(self, other) |
Logical Operators:
Operator | Method without underscores | Method Signature |
---|---|---|
& | and |
__and__(self, other) |
| | or |
__or__(self, other) |
^ | xor |
__xor__(self, other) |
~ | invert |
__invert__(self) |
Indexing and Slicing Operators:
Operator | Method |
---|---|
obj[key] | __getitem__(self, key) |
obj[key] = value | __setitem__(self, key, value) |
del obj[key] | __delitem__(self, key) |
Unary Operators:
Operator | Method |
---|---|
- | __neg__(self) |
+ | __pos__(self) |
abs() | __abs__(self) |
~ | __invert__(self) |
Assignment Operators:
Operator | Method |
---|---|
+= | __iadd__(self, other) |
-= | __isub__(self, other) |
*= | __imul__(self, other) |
/= | __itruediv__(self, other) |
//= | __ifloordiv__(self, other) |
%= | __imod__(self, other) |
**= | __ipow__(self, other) |
Maybe that's why Python is slow – every time before executing an operator, it looks for a corresponding function in the class and all its parent classes. But this allows for the most compact code in the world :)
6.3 Indexing Operator
That you can compare objects or subtract sets is somewhat obvious. And you'll figure it out yourself if you're writing a class that implies logical or mathematical operations on it.
Let's go over an interesting example – the indexing operator. Let's jump straight into the code example:
class CustomList:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __repr__(self):
return repr(self.data)
# Usage
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1]) # Outputs: 2
c_list[1] = 10
print(c_list) # Outputs: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list) # Outputs: [1, 3, 4, 5]
Here we see an example of three operations:
Reading data through an index
Writing data through an index
And even deleting data through an index.
And it’s not at all necessary for the data inside to be stored as a list. Or that the index must be a number. For example, the dictionary
class is implemented just like that.
Creating a SuperList
Remember the list
class? You can assign elements to it, but only with indices that already exist. Let's create our own class, call it SuperList
, where you can access elements with any index:
If the index < 0, we'll insert the element at the beginning
If the index >= len, we'll add the element at the end
In other cases, just return the element
Example:
class SuperList(list):
def __init__(self, value):
super().__init__(value)
def __setitem__(self, index, value):
if index >= len(self):
super().append(value)
elif index < 0:
super().insert(0, value)
else:
super().__setitem__(index, value)
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst) # [99, 1, 2, 3, 100]
So, overloading indices is a great opportunity, and I recommend using it in practice. That's it for today.
GO TO FULL VERSION