3.5 - Special Methods and Operator Overloading
3.5.1 - Introduction to Special Methods
Special methods in Python, also known as magic or dunder methods, are methods with double underscores at the beginning and end of their names. They allow you to define how objects of your class interact with built-in Python operations.
Example 1: __str__
and __repr__
Methods
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __str__(self):
return f"{self.name}: ${self.price}"
def __repr__(self):
return f"Product('{self.name}', {self.price})"
p = Product("Widget", 50)
print(p) # Output using __str__: Widget: $50
print(repr(p)) # Output using __repr__: Product('Widget', 50)
Example 2: __len__
Method
class Inventory:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
inventory = Inventory(["widget", "gadget"])
print(len(inventory)) # Output: 2
3.5.2 - Operator Overloading
Operator overloading in Python allows you to redefine how built-in operators work with objects of a class. This is achieved by implementing special methods.
Example 1: Overloading Arithmetic Operators
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
p1 = Point(1, 2)
p2 = Point(2, 3)
p3 = p1 + p2
print(p3.x, p3.y) # Output: 3 5
Example 2: Overloading Comparison Operators
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __eq__(self, other):
return self.pages == other.pages
def __gt__(self, other):
return self.pages > other.pages
book1 = Book("Python 101", 100)
book2 = Book("Python Advanced", 150)
print(book1 == book2) # Output: False
print(book1 > book2) # Output: False
3.5.3 - Practical Use Cases of Special Methods
Understanding the practical use cases of special methods can greatly enhance the usability and integration of your custom classes within the Python ecosystem.
Example: Building a Custom Sequence Type
class MySequence:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return len(self.data)
seq = MySequence([1, 2, 3, 4, 5])
print(seq[2]) # Output: 3
print(len(seq)) # Output: 5
3.5.4 - Best Practices and Limitations
While special methods and operator overloading can make your classes more Pythonic and intuitive, it’s important to use them judiciously:
-
Maintain Expected Behavior: Overloaded operators should behave in ways that are consistent with their expected behavior to avoid confusion.
-
Avoid Overcomplicating: Use operator overloading only when it makes the code more readable and maintainable.