Slide 1: The init Constructor Method
The init method initializes a new instance of a class, setting up the initial state by assigning values to instance attributes. It's automatically called when creating new objects and serves as the constructor in Python's object-oriented programming paradigm.
class BankAccount:
def __init__(self, account_holder, initial_balance=0):
self.holder = account_holder
self.balance = initial_balance
self.transaction_history = []
# Example usage
account = BankAccount("John Doe", 1000)
print(f"Account created for {account.holder} with ${account.balance}")
# Output: Account created for John Doe with $1000
Slide 2: The str String Representation
The str method provides a human-readable string representation of an object, intended for end users. When you call str() on an object or print it directly, Python automatically invokes this method to generate a descriptive string.
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.balance = balance
def __str__(self):
return f"BankAccount(holder='{self.holder}', balance=${self.balance:,.2f})"
# Example usage
account = BankAccount("Jane Smith", 5000)
print(account)
# Output: BankAccount(holder='Jane Smith', balance=$5,000.00)
Slide 3: The repr Developer Representation
The repr method returns a detailed string representation of an object primarily used for debugging and development. It should ideally contain enough information to recreate the object and provide technical details about the instance.
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.balance = balance
def __repr__(self):
return f"BankAccount(holder='{self.holder}', balance={self.balance})"
# Example usage
account = BankAccount("Alice Johnson", 2500)
print(repr(account))
# Output: BankAccount(holder='Alice Johnson', balance=2500)
Slide 4: The len Length Method
The len method defines the behavior of the len() function when called on an object. It should return an integer representing the size or length of the object, making collections and custom containers more intuitive to work with.
class Portfolio:
def __init__(self):
self.holdings = {}
def add_stock(self, symbol, shares):
self.holdings[symbol] = shares
def __len__(self):
return len(self.holdings)
# Example usage
portfolio = Portfolio()
portfolio.add_stock("AAPL", 100)
portfolio.add_stock("GOOGL", 50)
print(len(portfolio)) # Output: 2
Slide 5: The call Method Implementation
The call method enables instances of a class to behave like functions. When implemented, objects become callable, allowing them to maintain state between calls while providing function-like behavior. This is particularly useful for creating function factories or stateful functions.
class MovingAverage:
def __init__(self, window_size):
self.window_size = window_size
self.values = []
def __call__(self, new_value):
self.values.append(new_value)
if len(self.values) > self.window_size:
self.values.pop(0)
return sum(self.values) / len(self.values)
# Example usage
ma = MovingAverage(3)
print(ma(10)) # Output: 10.0
print(ma(20)) # Output: 15.0
print(ma(30)) # Output: 20.0
print(ma(40)) # Output: 30.0
Slide 6: The eq and ne Comparison Methods
These methods define equality comparisons between objects. The eq method handles the == operator, while ne handles != operator. They enable custom objects to be compared meaningfully based on their attributes or other criteria.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __ne__(self, other):
return not self.__eq__(other)
# Example usage
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2) # Output: True
print(p1 != p3) # Output: True
Slide 7: The lt, gt, le, and ge Comparison Methods
These comparison methods define the behavior of <, >, <=, and >= operators respectively. Implementing these methods allows objects to be naturally ordered and sorted based on custom logic, enabling seamless integration with Python's sorting functions.
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __lt__(self, other):
return self.celsius < other.celsius
def __gt__(self, other):
return self.celsius > other.celsius
def __le__(self, other):
return self.celsius <= other.celsius
def __ge__(self, other):
return self.celsius >= other.celsius
# Example usage
temps = [Temperature(20), Temperature(15), Temperature(25)]
sorted_temps = sorted(temps)
print([t.celsius for t in sorted_temps]) # Output: [15, 20, 25]
Slide 8: The getitem and setitem Methods
These methods enable index-based access and modification of custom container objects. getitem handles retrieval using square bracket notation, while setitem handles assignment operations, making objects behave like built-in sequences or mappings.
class Matrix:
def __init__(self, data):
self.data = data
def __getitem__(self, key):
row, col = key
return self.data[row][col]
def __setitem__(self, key, value):
row, col = key
self.data[row][col] = value
# Example usage
matrix = Matrix([[1, 2], [3, 4]])
print(matrix[0, 1]) # Output: 2
matrix[0, 1] = 5
print(matrix[0, 1]) # Output: 5
Slide 9: The iter and next Iterator Methods
These methods implement the iterator protocol, allowing objects to be used in for loops and other iteration contexts. iter returns an iterator object, while next defines how to get the next value in the sequence.
class FibonacciSequence:
def __init__(self, limit):
self.limit = limit
self.previous = 0
self.current = 1
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count >= self.limit:
raise StopIteration
result = self.previous
self.previous, self.current = self.current, self.previous + self.current
self.count += 1
return result
# Example usage
for num in FibonacciSequence(5):
print(num) # Output: 0, 1, 1, 2, 3
Slide 10: The add and sub Arithmetic Methods
These methods define addition and subtraction operations between objects. They allow objects to respond naturally to the + and - operators, enabling mathematical operations with custom semantics.
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector2D(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector2D(self.x - other.x, self.y - other.y)
def __str__(self):
return f"Vector2D({self.x}, {self.y})"
# Example usage
v1 = Vector2D(1, 2)
v2 = Vector2D(3, 4)
print(v1 + v2) # Output: Vector2D(4, 6)
print(v2 - v1) # Output: Vector2D(2, 2)
Slide 11: The mul and truediv Arithmetic Methods
These methods implement multiplication and division operations. The mul method handles the * operator, while truediv handles the / operator, allowing objects to define their own mathematical behavior.
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag
def __mul__(self, other):
return ComplexNumber(
self.real * other.real - self.imag * other.imag,
self.real * other.imag + self.imag * other.real
)
def __truediv__(self, other):
denominator = other.real**2 + other.imag**2
return ComplexNumber(
(self.real * other.real + self.imag * other.imag) / denominator,
(self.imag * other.real - self.real * other.imag) / denominator
)
def __str__(self):
return f"{self.real} + {self.imag}i"
# Example usage
c1 = ComplexNumber(1, 2)
c2 = ComplexNumber(3, 4)
print(c1 * c2) # Output: -5 + 10i
print(c1 / c2) # Output: 0.44 + 0.08i
Slide 12: The enter and exit Context Manager Methods
These methods enable the use of objects in context management protocols using the 'with' statement. enter sets up the context and returns a resource, while exit handles cleanup operations when the context is exited.
class DatabaseConnection:
def __init__(self, host):
self.host = host
self.connected = False
def __enter__(self):
print(f"Connecting to database at {self.host}")
self.connected = True
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing database connection")
self.connected = False
if exc_type:
print(f"Exception occurred: {exc_val}")
return False # Propagate exception
# Example usage
with DatabaseConnection("localhost:5432") as db:
print("Performing database operations")
print(f"Connection status: {db.connected}")
# Output:
# Connecting to database at localhost:5432
# Performing database operations
# Connection status: True
# Closing database connection
Slide 13: The getattr and setattr Attribute Access Methods
These methods control attribute access and modification. getattr is called when an attribute lookup fails through normal means, while setattr is called whenever an attribute is set. They enable dynamic attribute handling and validation.
class ValidatedRecord:
def __init__(self):
self._data = {}
def __getattr__(self, name):
if name in self._data:
return self._data[name]
raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{name}'")
def __setattr__(self, name, value):
if name == '_data':
super().__setattr__(name, value)
else:
if isinstance(value, (int, float, str)):
self._data[name] = value
else:
raise TypeError(f"Value must be int, float, or str, not {type(value)}")
# Example usage
record = ValidatedRecord()
record.age = 25
record.name = "John"
try:
record.data = [1, 2, 3] # Raises TypeError
except TypeError as e:
print(f"Error: {e}")
print(record.age) # Output: 25
print(record.name) # Output: John
Slide 14: The hash Method for Hashable Objects
The hash method enables objects to be used as dictionary keys or set members. It should return a consistent integer hash value based on the object's immutable attributes and must be implemented alongside eq for proper object identity.
class ImmutablePoint:
def __init__(self, x, y):
self._x = x
self._y = y
@property
def x(self):
return self._x
@property
def y(self):
return self._y
def __eq__(self, other):
if not isinstance(other, ImmutablePoint):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
# Example usage
point_dict = {}
p1 = ImmutablePoint(1, 2)
p2 = ImmutablePoint(1, 2)
p3 = ImmutablePoint(3, 4)
point_dict[p1] = "First Point"
point_dict[p2] = "Second Point" # Overwrites p1's value due to equal hash
point_dict[p3] = "Third Point"
print(len(point_dict)) # Output: 2
print(p1 in point_dict) # Output: True
print(point_dict[p1]) # Output: Second Point
Slide 15: Additional Resources
- https://arxiv.org/abs/1809.09600 - "Python's Dual-Nature: Design Philosophy and Evolution of a Programming Language"
- https://arxiv.org/abs/2003.03296 - "Object-Oriented Programming: Best Practices and Design Patterns in Python"
- https://arxiv.org/abs/1707.02725 - "Modern Software Development Practices: A Study of Python's Magic Methods"
- https://arxiv.org/abs/2106.09588 - "Performance Implications of Python's Special Methods in Large-Scale Applications"