Customizing classes with special methods

Due to their double underscore naming pattern, special methods in Python are also referred to as magic methods or dunder methods. They are used to modify the behaviour of classes and allow them to communicate with the built-in Python functions and operations. The double underscore (‘_’) marks the beginning and conclusion of these functions. Unless you clearly want to modify some default behaviour, you should normally refrain from utilising this convention when naming your own methods or functions.
Special methods overload normal operators and built-in functions to enable user-defined classes to mimic standard types. Custom classes can employ comparison operators (<, >, ==,!=, etc.), operators (+, -, *, /), and functions (e.g., len() or str()) identical to built-in types like numbers or strings using defined methods. This makes utilising these procedures with objects intuitive, making code shorter and clearer.
Operator overloading
This intuitive syntax is made possible by specific methods using a concept known as operator overloading. To implement + in a user-defined class, you must implement a method mapped to the class user’s operator. The < and > comparison operators are assigned to lt and gt, while +, -, and * are assigned to add, sub, and mul.
Here are some common special methods
init(self,): This constructor method is used to set up new class instances (objects). The instance that is being created is referenced by the first parameter, which is typically called self. Arguments given when generating the object are any additional parameters.
Self:The built-in str() and print() functions employ str(self) to identify an object’s “informal” or well-printed string representation. You can control print appearance by defining this technique
Comparison methods: These methods specify how instances of your class are compared. For lt(self, other), examples include <, gt for >, le for <=, ge for >=, eq for ==, and ne for=.
Arithmetic methods: These methods specify how instances of your class act with arithmetic operators. Examples are add(self, other) for +, sub for -, mul for *, truediv for /, and mod for %.
len (self): Built-in len() calls len(self) to acquire object length.
Let’s demonstrate class method addition using an example. When working with mathematical objects such as fractions or rational numbers, operator overloading is frequently helpful. In order to facilitate initialisation, string representation, and addition, we can construct a Rational class and specify specific methods.
Example demonstrating customization with special methods import math # Needed for gcd
class Rational:
Represents a rational number with numerator and denominator.
def init (self, numerator=0, denominator=1): # Define the constructor
Initializes a Rational object with numerator and denominator.
Denominator cannot be zero.
# Ensure denominator is not zero and is an integer
if not isinstance(denominator, int) or denominator == 0:
raise ValueError("Denominator must be a non-zero integer")
# Ensure numerator is an integer
if not isinstance(numerator, int):
raise ValueError("Numerator must be an integer")
# Simplify the fraction upon initialization
common divisor = math.gcd(numerator, denominator) # Use math.gcd to find greatest common divisor
self. numerator = numerator // common divisor # Private attribute for numerator
self. denominator = denominator // common divisor # Private attribute for denominator
def str (self): # Define the string representation method
Returns a string representation of the rational number.
if self. denominator == 1:
return str(self. numerator)
else:
return f"{self. numerator}/{self. denominator}"
def add (self, other): # Define the addition method for the '+' operator
Adds two Rational objects or a Rational object and an integer.
if isinstance(other, int):
# Add a Rational and an integer: a/b + c = (a + c*b) / b
new numerator = self. numerator + other self. denominator
new denominator = self. denominator
return Rational(new numerator, new denominator)
elif isinstance(other, Rational):
# Add two Rational numbers: a/b + c/d = (a*d + c*b) / (b*d)
new numerator = self. numerator other. denominator + other. numerator * self. denominator
new denominator = self. denominator other. denominator
return Rational(new numerator, new denominator)
else:
# Handle unsupported types for addition
return NotImplemented # Indicates that the operation is not supported for this type
# Example of a comparison method (Less than)
def lt (self, other): # Define the less than method for the '<' operator
Compares if this Rational object is less than another object.
if isinstance(other, int):
# Compare Rational and integer: a/b < c is equivalent to a < c*b
return self. numerator * other. denominator < other * self. denominator
elif isinstance(other, Rational):
# Compare two Rational numbers: a/b < c/d is equivalent to a*d < c*b
return self. numerator * other. denominator < other. numerator * self. denominator
else:
return NotImplemented # Indicates that the operation is not supported for this type
# Using the customized Rational class
# Create instances using the constructor init ()
r1 = Rational(1, 2)
r2 = Rational(3, 4)
r3 = Rational(6, 8) # This will be simplified to 3/4 by init
# Print instances using the str () method
print(f"r1: {r1}") # Output: r1: 1/2
print(f"r2: {r2}") # Output: r2: 3/4
print(f"r3: {r3}") # Output: r3: 3/4
# Add instances using the overloaded '+' operator (calls add ())
sum r1 r2 = r1 + r2 # 1/2 + 3/4 = 2/4 + 3/4 = 5/4
print(f"{r1} + {r2} = {sum r1 r2}") # Output: 1/2 + 3/4 = 5/4
# Add an instance and an integer (calls add ())
sum r1 int = r1 + 1 # 1/2 + 1 = 3/2
print(f"{r1} + 1 = {sum r1 int}") # Output: 1/2 + 1 = 3/2
# Compare instances using the overloaded '<' operator (calls lt ())
print(f"{r1} < {r2}: {r1 < r2}") # Output: 1/2 < 3/4: True
print(f"{r1} < {r3}: {r1 < r3}") # Output: 1/2 < 3/4: True
print(f"{r2} < {r1}: {r2 < r1}") # Output: 3/4 < 1/2: False
In this example
- A Rational object can be initialised with a numerator and denominator using the init method. It also simplifies the fraction and provides basic validation.
- The str method specifies how the object is printed as a string.
- You can add rational objects naturally because the add method overloads the + operator for them. Additionally, it has logic for handling the addition of an integer to a rational number.
- Rational objects and integers can be compared with the lt method, which overloads the < operator.
The Rational class implements these special methods so that when used with standard operators and functions, it operates similarly to Python’s built-in numeric types, making the code intelligible and easy to understand. You can specify how your objects behave in the larger Python environment with this customisation.