Table of Contents#
- Understanding Python’s Default Class Behavior
- Do User-Defined Classes Have
__cmp__()by Default? - Do User-Defined Classes Have
__hash__()by Default? - Resolving the Docs vs. Code Discrepancy
- Practical Implications for Developers
- Conclusion
- References
1. Understanding Python’s Default Class Behavior#
In Python, every class implicitly inherits from object (the root of the class hierarchy) unless specified otherwise. The object class provides a set of default behaviors for basic operations like initialization (__init__), string representation (__str__, __repr__), and comparison. However, not all special methods are implemented by object—and thus not inherited by user-defined classes by default.
To determine if a user-defined class has __cmp__() or __hash__() by default, we must examine:
- What methods does the base
objectclass actually implement? - How do user-defined classes inherit or override these methods?
2. Do User-Defined Classes Have __cmp__() by Default?#
The Role of __cmp__()#
__cmp__() is an older special method historically used to define a single comparison function for objects. It returns a negative number if self < other, zero if self == other, and a positive number if self > other. However, __cmp__() is deprecated in Python 3 in favor of "rich comparison" methods like __eq__() (equality), __lt__() (less than), __gt__() (greater than), etc.
Default Behavior in User-Defined Classes#
The base object class does not implement __cmp__(). Thus, user-defined classes inheriting from object (the default) also do not have __cmp__() by default.
Example: Testing for __cmp__()#
class MyClass:
pass # No custom methods
obj = MyClass()
# Attempt to call __cmp__()
try:
obj.__cmp__(obj)
except AttributeError as e:
print(e) # Output: 'MyClass' object has no attribute '__cmp__'This confirms that __cmp__() is not present in user-defined classes by default. Instead, Python uses identity-based comparisons for instances of user-defined classes:
==and!=check for identity (viaid()), not value, unless__eq__()is explicitly defined.- Ordering operations (
<,>, etc.) raise aTypeErrorunless__lt__()or other rich comparison methods are defined.
Key Takeaway#
User-defined classes do not have __cmp__() by default. Python 3 relies on rich comparison methods (e.g., __eq__, __lt__) instead, with default comparisons based on object identity.
3. Do User-Defined Classes Have __hash__() by Default?#
The Role of __hash__()#
__hash__() returns an integer hash value for an object, enabling it to be used as a key in dictionaries or stored in sets. For an object to be hashable, its hash value must remain constant during its lifetime (i.e., it must be "immutable" in terms of comparison behavior).
Default Behavior: It Depends on __eq__()#
The presence and behavior of __hash__() in user-defined classes depend on whether __eq__() (equality) is explicitly defined:
Case 1: No Custom __eq__()#
If a user-defined class does not define __eq__(), it inherits __hash__() from object. The default __hash__() uses the object’s identity (via id()) to compute the hash, ensuring unique hashes for distinct instances.
Example: Default __hash__() with No __eq__
class HashableClass:
pass # No __eq__ defined
obj1 = HashableClass()
obj2 = HashableClass()
print(hash(obj1)) # Output: Some integer (e.g., 140425465762336)
print(hash(obj2)) # Output: A different integer (e.g., 140425465762384)
print(obj1 == obj2) # Output: False (identity check via id())Case 2: Custom __eq__() but No __hash__()#
If a class defines __eq__() but not __hash__(), Python automatically sets __hash__ = None, making instances unhashable. This is because hashability requires consistency: if two objects are equal (__eq__ returns True), they must have the same hash. Without a custom __hash__(), Python cannot guarantee this, so it disables hashing.
Example: __hash__ Becomes None with Custom __eq__
class UnhashableClass:
def __eq__(self, other):
return isinstance(other, UnhashableClass) # All instances are "equal"
obj = UnhashableClass()
# Check if __hash__ exists
print(UnhashableClass.__hash__) # Output: None
# Attempt to hash the object
try:
hash(obj)
except TypeError as e:
print(e) # Output: unhashable type: 'UnhashableClass'Case 3: Custom __eq__() and __hash__()#
If you define both __eq__() and __hash__(), instances remain hashable, provided __hash__() is consistent with __eq__().
Example: Explicit __hash__() Restores Hashability
class HashableAgain:
def __eq__(self, other):
return isinstance(other, HashableAgain)
def __hash__(self):
return 42 # All instances share the same hash (simplified example)
obj = HashableAgain()
print(hash(obj)) # Output: 42 (hashable again!)Key Takeaway#
User-defined classes have __hash__() by default only if __eq__() is not explicitly defined. Defining __eq__() without __hash__() sets __hash__ = None, making instances unhashable.
4. Resolving the Docs vs. Code Discrepancy#
A common source of confusion is the perceived discrepancy between Python’s documentation and real-world code behavior. Let’s clarify:
Discrepancy 1: __cmp__() in Older Docs#
Some legacy Python 2 documentation references __cmp__(), leading developers to assume it exists in Python 3. However, Python 3 deprecated __cmp__() in favor of rich comparisons, and the base object class no longer includes it. Modern docs (e.g., Python 3 Data Model) explicitly omit __cmp__().
Discrepancy 2: __hash__() and __eq__() Interaction#
The Python docs state:
If a class defines
__eq__()but not__hash__(), then__hash__()will be implicitly set toNone, making instances of the class unhashable. (Source)
This aligns with our earlier examples, but developers may miss this detail, leading to surprise when instances suddenly become unhashable after defining __eq__().
Resolution#
The "discrepancy" arises from outdated references or incomplete reading of modern docs. The key is to:
- Ignore
__cmp__()in Python 3; use rich comparison methods instead. - Remember that defining
__eq__()overrides the default__hash__()behavior.
5. Practical Implications for Developers#
For __cmp__()#
- Avoid
__cmp__()in Python 3: Use__eq__,__lt__, etc., for comparisons. - Default comparisons are identity-based: Use
id(obj1) == id(obj2)to check for identity, or define__eq__()to compare values.
For __hash__()#
- Hashing requires consistency: If you define
__eq__(), always define__hash__()to ensure instances remain hashable (if intended). - Immutable objects: For hashable objects (e.g., custom "value types"), ensure
__hash__()depends only on immutable attributes.
6. Conclusion#
User-defined classes in Python behave as follows by default:
__cmp__(): Not present. Python 3 uses rich comparison methods (e.g.,__eq__) with identity-based defaults.__hash__(): Present only if__eq__()is not defined. Defining__eq__()without__hash__()makes instances unhashable.
By understanding these defaults and consulting modern Python documentation, developers can avoid common pitfalls and write more predictable code.
7. References#
- Python 3 Data Model:
__hash__() - Python 3 Data Model: Comparison Operations
- PEP 207: Rich Comparisons (deprecated
__cmp__in favor of rich comparisons) - Python 3 Docs:
__cmp__(Legacy Note)