Why this matters
Programs compare things all the time. Is the password the one we stored? Is this product already in the cart? Is this customer the same one as before? Programs also print things, such as an order or a price, so a person can read them.
By default, a class you write does not know how to answer any of these questions. In Python, == on two objects of your own class checks whether they are the same object in memory, which is often not what you want. The operator + does not work on your objects at all, and sorted does not know how to order them.
This lesson shows how to tell Python what "equal", "less than", "plus" and "printed form" mean for your own class. Python calls these operators through special methods, whose names start and end with two underscores. The lesson uses one small example, a Money class that holds an amount in cents, and builds it up. The lesson on immutable objects, which comes just before this one, explains why a value like Money is a good fit for equality checks.
The default: two objects are two identities
Start with a class that does nothing special.
The output is:
a == b: False
a is b: FalseBoth objects hold 1050 cents, yet Python says they are not equal. The reason is that a and b are two separate objects in memory. For a class that does not define __eq__, the operator == falls back to the same check as is. The is operator asks whether two names point to the same object. The == operator asks whether the values are equal, and it uses the special method __eq__ to answer. Without one, the answer is the identity check.
To make == compare the amounts instead, the class has to define __eq__. The next sections do that.
Operators are method calls
In Python, a == b is shorthand for a call to a.__eq__(b). The expression a + b calls a.__add__(b). The expression a < b calls a.__lt__(b). The built-in functions do the same: len(a) calls a.__len__(), and print(a) calls str(a), which calls a.__str__(). A class can define any of these special methods to change what the operator does for its objects. This is called operator overloading. "Overloading" means giving an existing operator a new meaning for a new type.
Here is the Money class with the methods it needs. Each one is explained after the example.
The output is:
a = $10.50
a == b: True
a != c: True
a + c = $13.00
a is b: FalseSome details in this code need explaining.
__eq__takes the other object as a parameter and returnsTrueorFalse. The checkisinstance(other, Money)makes sure the other value is also aMoney. If it is not, the method returnsNotImplemented. That special value tells Python to try the other side of the comparison. Returning it, rather thanFalse, lets another class decide.!=needs no method of its own. Python builds it from__eq__. If two values are not equal, they are different, so the rule is written only once.__add__builds and returns a newMoney. It does not changeaorc, which is the copy on change pattern from the immutable objects lesson.__repr__gives the text form for developers, such as the list output shown later.__str__gives the text form for people, andprintuses it. If a class defines only__repr__,printfalls back to it.- The format
{self._cents % 100:02d}pads the cents to two digits with a leading zero, so 5 cents prints as05.
Comparing with other types
The __eq__ method returns NotImplemented for values of another type. Here is what that means in practice:
The output is:
False
TrueA Money never equals a string, even when the text looks the same. Python tries Money.__eq__, gets NotImplemented, then tries the string's side, which also says no. The result is False. Returning False directly would also work for this class, but returning NotImplemented is the standard choice, because it lets the other type take part in the comparison.
Ordering: __lt__ and sorted
Python sorts a list by comparing its items with <. A class that defines __lt__, which means "less than", can be sorted with sorted and list.sort, and it can be used with min and max. The next example sorts a list of money values.
The output is:
[Money(120), Money(500), Money(900)]
Money(120) Money(900)The function sorted returns a new list, and the original list is not changed. Python's sort uses only <, so __lt__ is enough for it. The square brackets show the repr text, because a list displays its items with repr.
When you need >, <= and >= as well, write them by hand, or use functools.total_ordering. That decorator, a line starting with @ that changes the class below it, fills in the missing comparison methods when you write __eq__ and one ordering method.
The output is:
True
FalseNeither >= nor > is written in the class. The decorator builds them from __eq__ and __lt__.
A tuple compares its items from left to right, and it is a convenient way to write a rule with several parts. For example, (self.year, self.title) < (other.year, other.title) sorts by year first and by title when the years match, as the interfaces lesson showed.
Hashing: the rule that links __eq__ and __hash__
Python sets and dictionaries use a hash code to find where an object is stored. A hash code is a whole number that the object gives for itself. Python uses it to pick a small group of stored objects to look at, and then it uses == to find the match inside that group.
The rule that joins the two methods is this. If two objects are equal, they must have the same hash code. The reverse is not required, because two different objects can share a hash code. Python makes sure that a class with __eq__ and no __hash__ cannot be stored in a set or used as a dictionary key. The next example shows the error:
The output is:
TypeError: cannot use 'NoHash' as a set element (unhashable type: 'NoHash')When a class defines __eq__, Python sets its __hash__ to None unless the class also defines __hash__. The message says the class is unhashable. The fix is to define __hash__ with the same fields that __eq__ uses:
The output is:
set size: 1
contains c-7: TrueTwo Key objects with the same id are equal by __eq__, and they have the same hash code, so the set keeps one of them. A lookup with a new object finds it.
If __eq__ and __hash__ disagreed, the set would hold two copies of the same key, or would fail to find one that it holds. The rule that equal objects must share a hash code is what keeps the two in step.
A key that changes after it is stored
A set also depends on the hash code not changing while the object sits inside it. Here is a key whose field can be changed.
The output is:
before change: True
after change, contains a1: False
after change, contains b2: FalseThe object was stored in a group chosen by the hash code of "a1". After k.item_id changed to "b2", its hash code changed too, so a lookup now looks in a different group and does not find it. The object is still inside the set, but the set cannot find it. The common fix is to make the fields used by __eq__ and __hash__ read-only, for example with a frozen dataclass, so they cannot change after the object is built.
Text forms: __str__ and __repr__
Python has two text forms. str is for people, and print uses it. repr is for developers, and it should show enough detail to rebuild the object. The interactive prompt and the list display use repr. Both are optional. If you define only __repr__, Python uses it for both.
The output is:
(4, -2)
[Point(4, -2)]
Point(4, -2)The first line uses __str__, because print calls str. The second line shows a list, which uses repr for each item. The third line uses !r in the f-string to ask for the repr form on purpose.
A value class with a full set of operators
Putting the pieces together, a small value class looks like this. A frozen dataclass can write most of this for you, as the immutable objects lesson showed. The version below writes each method by hand so that you can see what each one does.
The output is:
distinct amounts: 2
sorted: [Money(250), Money(1050)]
total: $13.00
text: $0.05The set holds two items, because the two Money(1050) objects are equal and share a hash code. The sort uses __lt__. The + uses __add__. The text form for people comes from __str__.
Java and C++ comparison
Java and C++ use different names for the same jobs. In Java, a class overrides equals, hashCode and toString, and Comparable provides ordering. In C++, the class defines operator==, operator< and operator<<. Python uses the special methods __eq__, __hash__, __lt__ and __str__, which play the same roles. One difference matters most. In Java, equals exists on every object, and the default compares identity. In Python, == also compares identity by default, and a class that defines __eq__ without __hash__ loses the ability to be stored in a set.
Common mistakes
Defining __eq__ without __hash__
The NoHash example above shows the result. A class with __eq__ and no __hash__ cannot be used in a set or as a dictionary key. The message ends with unhashable type: .... Define __hash__ with the same fields, or make the class a frozen dataclass.
Using is to compare values
The is operator checks whether two names point to the same object. For numbers, text and your own value objects, use ==. Use is for None, as in if value is None, because None is a single shared object.
The output of the first line is False, because Money(1050) builds a new object. The second line is True.
Returning False for a different type in a way that hides a bug
Returning False directly from __eq__ for every other type is simple, and it works for most classes. The risk is that a comparison with a value of the wrong type hides a bug quietly. The standard choice is NotImplemented, which lets Python report a TypeError when no other side can answer an ordering comparison, such as <.
Forgetting __lt__ before sorting
A list of your own objects cannot be sorted until the class defines __lt__. Python raises an error that names the two types:
The output is:
TypeError: '<' not supported between instances of 'Book' and 'Book'The fix is to define __lt__ on the class, or to pass a key function to sorted, such as sorted(books, key=lambda b: b.title).
Printing an object and seeing only its address
A class without __str__ or __repr__ shows the default text, which includes the class name and an address in memory. The address is different on every run and on every machine, so the output cannot be checked. Define __repr__ for any class you print.
Debugging walkthrough: two equal objects are treated as different
Suppose a set holds two objects that look the same, or a lookup fails for an object that is in the set. Use this routine.
- Check that the class defines
__eq__. If it does not,==compares identity, and two separate objects are never equal. - Check that
__eq__compares the fields that define the value, and that it returnsNotImplementedorFalsefor other types. - Check that
__hash__is defined, and that it uses the same fields as__eq__. - Check that the fields used by
__hash__do not change while the object is in a set or a dictionary. - Print the hash codes of the two objects, with
hash(a)andhash(b). Equal objects must print the same number.
Variations and edge cases
Python lets an operator work with a value on the left or the right. The method __radd__ handles 5 + money, when the left value does not know how to add. This is rarely needed in value classes, but it is how libraries such as numeric arrays combine with plain numbers.
A class can define __bool__ to say when an object counts as true, such as an empty collection. Without it, Python uses __len__ if the class defines one, and otherwise every object counts as true.
A class can define __contains__ so that x in obj works. A class can define __len__ so that len(obj) works. These follow the same pattern as the operators above.
Edge case: a comparison with None. Money(5) == None returns False with the __eq__ above, because None is not a Money. If a value might be None, check for it before you call a method on it.
Interview-style questions
What is the difference between == and is in Python? == asks whether two values are equal, and it calls __eq__. is asks whether two names point to the same object.
What must hold between __eq__ and __hash__? Two equal objects must have the same hash code. A class that defines __eq__ must also define __hash__ to be used in a set or as a dictionary key.
Why does a class that defines __eq__ lose its __hash__? Python sets __hash__ to None to stop the class from being used in a set when the rule above might be broken. Defining __hash__ with the same fields restores it.
What does NotImplemented do in __eq__? It tells Python that this class cannot answer, so Python tries the other side. It is different from False, which is a final answer.
What is the difference between __str__ and __repr__? __str__ is the text for people, and print uses it. __repr__ is the text for developers, and lists and the interactive prompt use it.
How does sorted compare two objects? It uses <, which calls __lt__. A class that defines only __lt__ is enough for sorted, min and max.
Real-world uses
Value classes such as money, dates, coordinates and identifiers need equality and text forms. A payments library compares amounts with == and sums them with +, and it prints them with __str__ in reports. A geometry library defines + and - for vectors, so the code reads like the maths. A cache or a deduplication step stores objects in a set, and it depends on __eq__ and __hash__ agreeing.
Practice
Exercise 1
Write a class Point with two attributes, x and y. Define __eq__, __hash__ and __repr__ so that Point(1, 2) prints as Point(1, 2), two points with the same x and y are equal, and a set holding two such points has size 1.
Show the answer
The output is Point(1, 2), then True, then 1. The hash uses a tuple of the same two fields that __eq__ compares.
Exercise 2
The class BadKey below has an __eq__ but no __hash__. Add the missing method so that a set of two keys with the same id has size 1. Do not change __eq__.
class BadKey:
def __init__(self, item_id):
self.item_id = item_id
def __eq__(self, other):
return isinstance(other, BadKey) and self.item_id == other.item_idShow the answer
Add this method inside BadKey:
def __hash__(self):
return hash(self.item_id)With the method added, the two keys share a hash code, and the set size is 1.
Exercise 3
Write a class Money with __lt__ so that a list of three Money values holding 500, 120 and 900 cents prints in order from smallest to largest. Use the cents value only, and print each amount on its own line.
Show the answer
The output is 120, then 500, then 900.
Exercise 4
Write a __str__ method for a Money class that holds cents, so that Money(5) prints as $0.05, and Money(1050) prints as $10.50.
Show the answer
The output is $0.05, then $10.50. The format :02d pads the cents to two digits with a leading zero.
Exercise 5
A class Vector holds x and y. Define __add__ so that Vector(1, 2) + Vector(3, 4) gives a new Vector(4, 6), and define __repr__ so that the result prints as Vector(4, 6). Check that the original vectors do not change.
Show the answer
The output is Vector(4, 6), then Vector(1, 2) Vector(3, 4). The method __add__ builds a new object, so a and b keep their values.
Summary
- Python operators call special methods.
==calls__eq__,+calls__add__,<calls__lt__, andprintcalls__str__. - Without
__eq__,==compares identity, so two separate objects are never equal. - A class that defines
__eq__must define__hash__with the same fields, or it cannot be used in a set or as a dictionary key. - Return
NotImplementedfrom__eq__for other types, so Python can try the other side. sorted,minandmaxuse__lt__.functools.total_orderingfills in the other comparisons.__str__is the text for people, and__repr__is the text for developers.