Home > Article > Backend Development > Learn Python Magic Methods: A Simple Explanation
Magic methods in Python, also known as dunder methods (because they have double underscores at the beginning and end of their names), allow us to define the behavior of our objects for various operations. They enable custom behavior and can make our classes act like built-in types. In this blog, we will explore different categories of magic methods, provide detailed explanations, and give practical examples and use cases.
These magic methods control how attributes of your objects are accessed, modified, or deleted.
__getattr__: Called when an attribute is not found in an object.
__getattribute__: Called unconditionally to access any attribute.
Example: Custom Attribute Access with Logging
class LoggedAttributes: def __init__(self, name): self.name = name def __getattr__(self, item): print(f"Accessing non-existent attribute: {item}") return None def __getattribute__(self, item): print(f"Getting attribute: {item}") return super().__getattribute__(item) # Usage obj = LoggedAttributes("Alice") print(obj.name) # Output: Getting attribute: name\nAlice print(obj.age) # Output: Accessing non-existent attribute: age\nNone
Practical Use Case: Logging attribute access in a debugging scenario to trace when and how attributes are accessed or modified.
__setattr__: Called when an attribute assignment is attempted.
__delattr__: Called when an attribute deletion is attempted.
Example: Custom Attribute Modification with Validation
class Person: def __init__(self, name, age): self.name = name self.age = age def __setattr__(self, key, value): if key == "age" and value < 0: raise ValueError("Age cannot be negative") super().__setattr__(key, value) def __delattr__(self, item): if item == "name": raise AttributeError("Can't delete attribute 'name'") super().__delattr__(item) # Usage p = Person("Alice", 30) p.age = 25 # Works fine # p.age = -1 # Raises ValueError # del p.name # Raises AttributeError
Practical Use Case: Enforcing validation rules or restrictions when setting or deleting attributes.
These magic methods allow your objects to behave like containers (lists, dictionaries, etc.).
__len__: Returns the length of the container.
__getitem__: Retrieves an item at a given index or key.
__setitem__: Sets an item at a given index or key.
__delitem__: Deletes an item at a given index or key.
__iter__: Returns an iterator object.
Example: Custom List-like Object
class CustomList: def __init__(self): self._items = [] def __len__(self): return len(self._items) def __getitem__(self, index): return self._items[index] def __setitem__(self, index, value): self._items[index] = value def __delitem__(self, index): del self._items[index] def __iter__(self): return iter(self._items) def append(self, item): self._items.append(item) # Usage cl = CustomList() cl.append(1) cl.append(2) cl.append(3) print(len(cl)) # Output: 3 print(cl[1]) # Output: 2 for item in cl: print(item) # Output: 1 2 3
Practical Use Case: Creating a custom collection class that needs specialized behavior or additional methods while still supporting standard list operations.
These methods define how objects of your class interact with numeric operations and comparisons.
Example: Custom Complex Number Class
class Complex: def __init__(self, real, imag): self.real = real self.imag = imag def __add__(self, other): return Complex(self.real + other.real, self.imag + other.imag) def __sub__(self, other): return Complex(self.real - other.real, self.imag - other.imag) def __repr__(self): return f"({self.real} + {self.imag}i)" # Usage c1 = Complex(1, 2) c2 = Complex(3, 4) print(c1 + c2) # Output: (4 + 6i) print(c1 - c2) # Output: (-2 + -2i)
Practical Use Case: Implementing custom numeric types like complex numbers, vectors, or matrices.
Example: Implementing Total Ordering for a Custom Class
from functools import total_ordering @total_ordering class Book: def __init__(self, title, author): self.title = title self.author = author def __eq__(self, other): return (self.title, self.author) == (other.title, other.author) def __lt__(self, other): return (self.title, self.author) < (other.title, other.author) def __repr__(self): return f"{self.title} by {self.author}" # Usage book1 = Book("Title1", "Author1") book2 = Book("Title2", "Author2") books = [book2, book1] print(sorted(books)) # Output: [Title1 by Author1, Title2 by Author2]
Practical Use Case: Enabling custom objects to be sorted or compared, useful in data structures like heaps, binary search trees, or simply when sorting lists of custom objects.
Creating a dictionary-like object that treats keys as case-insensitive.
Example: Case-Insensitive Dictionary
class CaseInsensitiveDict: def __init__(self): self._data = {} def __getitem__(self, key): return self._data[key.lower()] def __setitem__(self, key, value): self._data[key.lower()] = value def __delitem__(self, key): del self._data[key.lower()] def __contains__(self, key): return key.lower() in self._data def keys(self): return self._data.keys() def items(self): return self._data.items() def values(self): return self._data.values() # Usage cid = CaseInsensitiveDict() cid["Name"] = "Alice" print(cid["name"]) # Output: Alice print("NAME" in cid) # Output: True
Practical Use Case: Creating dictionaries where keys should be treated as case-insensitive, useful for handling user inputs, configuration settings, etc.
Magic methods provide a powerful way to customize the behavior of your objects in Python. Understanding and effectively using these methods can make your classes more intuitive and integrate seamlessly with Python's built-in functions and operators. Whether you're implementing custom numeric types, containers, or attribute access patterns, magic methods can greatly enhance the flexibility and functionality of your code
The above is the detailed content of Learn Python Magic Methods: A Simple Explanation. For more information, please follow other related articles on the PHP Chinese website!