Quark's Outlines: Python Basic Customization
Source: Dev.to
Overview, Historical Timeline, Problems & Solutions
When you make a class in Python, you can change how your objects behave in certain common situations. This is called Python basic customization. Python gives you special method names that begin and end with double underscores. These methods let you control what happens when your object is:
- created
- shown (printed)
- compared
- deleted
- tested for truth
You do not call these methods yourself—Python calls them at the right time. You only define what they do.
Example: __init__ and __str__
class Greeter:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Hello, {self.name}!"
g = Greeter("Ada")
print(g)
# prints:
# Hello, Ada!
Here, Python calls __init__ when the object is created, and __str__ when it is printed.
Common Special (Magic) Methods
| Method | When it runs |
|---|---|
__init__ | Object creation |
__del__ | Object deletion (when reference count reaches zero) |
__str__ | print() or str() |
__repr__ | repr() or interactive interpreter display |
__cmp__ | Python 2: generic comparisons |
__eq__, __lt__, __le__, __gt__, __ge__, __ne__ | Python 3: rich comparisons |
__hash__ | When the object is used as a dictionary key or set element |
__bool__ (or __nonzero__ in Python 2) | Truth‑value testing (if obj:) |
You can define any of these methods in your class to make your objects behave in helpful ways.
Hooking into Core Actions with Magic Methods
class Number:
def __init__(self, value):
self.value = value
def __bool__(self):
return self.value != 0
print(bool(Number(0))) # False
print(bool(Number(5))) # True
This object tells Python whether it should be treated as True or False based on its internal value.
Where Do Python’s Customization Methods Come From?
Python built its special methods on ideas from earlier object‑oriented languages.
| Year | Milestone |
|---|---|
| 1980 | Smalltalk and C++ introduced special method names for construction, destruction, and string representation. |
| 1989 | Python’s class model adopted __init__, __del__, and __str__. |
| 1991 | Python 0.9.0 added __cmp__ and __repr__ for comparisons and debugging views. |
| 1995 | Truth testing added __nonzero__ (later replaced by __bool__ in Python 3). |
| 2001 | __hash__ rules refined in Python 2.2 for predictable dictionary behavior. |
| 2008 | Python 3.0 removed __cmp__, replacing it with rich‑comparison methods (__eq__, __lt__, …). |
| 2025 | Core method names are stable; recent changes focus on performance or internal use only. |
How to Use Python Basic Customization the Right Way
Below are common problems and their solutions, each illustrating a particular magic method.
1. Run code when an object is created
Problem: You want the name to be saved at the moment the object is made, without calling a separate method later.
Solution: Define __init__.
class User:
def __init__(self, name):
self.name = name
u = User("Ada")
print(u.name) # prints: Ada
2. Show a readable string when the object is printed
Problem: Printing an instance yields something like “. You want a clean, user‑friendly representation.
Solution: Define __str__.
class Report:
def __init__(self, title):
self.title = title
def __str__(self):
return f"Report: {self.title}"
r = Report("Q2 Sales")
print(r) # prints: Report: Q2 Sales
3. Clean up resources when an object is destroyed
Problem: An object uses a file or network resource; you need to ensure it’s closed when the object is no longer needed.
Solution: Define __del__.
class Connection:
def __del__(self):
print("Connection closed.")
c = Connection()
del c # prints: Connection closed.
Note: Relying on
__del__alone can be fragile (e.g., circular references). For deterministic cleanup, prefer context managers (withstatements) and the__enter__/__exit__protocol.
4. Compare objects
Problem: You have a class for items with prices and need to compare them.
Solution: In Python 3, implement the rich‑comparison methods you need (e.g., __lt__, __eq__).
class Item:
def __init__(self, price):
self.price = price
def __lt__(self, other):
return self.price < other.price
a = Item(10)
b = Item(15)
print(a < b) # prints: True
If you need full ordering, you can inherit from functools.total_ordering and implement __eq__ plus one other comparison.
5. Use objects as dictionary keys
Problem: You want instances of a custom class to serve as keys in a dict, requiring a stable hash that respects equality.
Solution: Implement __hash__ (and __eq__) consistently.
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, self.y) == (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
my_dict = {p1: "origin"}
print(my_dict[p2]) # prints: origin
Summary
Python’s magic methods (those with double underscores) let you hook into the language’s core operations—creation, representation, comparison, truth testing, hashing, and cleanup. By defining the appropriate methods in your classes, you can make your objects behave naturally and intuitively in everyday Python code.
class Point:
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
def __hash__(self):
return hash((self.x, self.y))
p = Point(1, 2)
d = {p: "here"}
print(d[p])
# prints:
# here
Python calls __hash__ to find where the object should live in the dictionary.
Mike Vincent is an American software engineer and app developer from Los Angeles, California. More about Mike Vincent.