Special Methods: str and Friends
A new phone learns your face, your fingerprint, and your voice. After that, it responds to you the way you expect, without you pressing any buttons.
At the end of Classes and Objects, printing an object gave something like <__main__.Student object at 0x0000021F3A8B5E50>. And if you've tried comparing two objects with ==, you may have noticed that two students with the same name and scores aren't "equal."
That's because Python doesn't know what your class means. Special methods are how you tell it: how your objects should print, compare, and behave with built-in functions like len(), so they work as smoothly as Python's own types.
Teaching your phone
A brand-new phone doesn't know who you are. Once you teach it your fingerprint, though, it responds to a simple touch. You didn't change how you use the phone; you taught it what "you" means.
Special methods work the same way. print(student), a == b, and len(team) are the everyday gestures. Your class teaches Python how to respond to each one, by defining a method with a special name.
Dunder names
Special methods have names that start and end with two underscores, like __init__, which you've been writing since Classes and Objects. Python programmers call them dunder methods, short for "double underscore": __init__ is read "dunder init."
You never call these methods by name yourself. Python calls them for you, at the right moment: __init__ when an object is created, __str__ when it's printed, and so on.
__str__: how it prints
Define __str__ to return the text you want print (and str(), and f-strings) to show:
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __str__(self):
return f"{self.name} (grade {self.grade})"
maria = Student("Maria", 11)
print(maria)
print(f"Top student: {maria}")Maria (grade 11)
Top student: Maria (grade 11)__str__ must return a string, not print it. Python takes the string it gives back and does the printing.
__repr__: how it looks to programmers
There's a second way to turn an object into text, meant for programmers rather than users. Here's where you'll notice it:
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __str__(self):
return f"{self.name} (grade {self.grade})"
team = [Student("Maria", 11), Student("Ben", 10)]
print(team[0])
print(team)The first print shows Maria (grade 11), as expected. But the second shows something like [<__main__.Student object at 0x000001A2B3C4D5E0>, <__main__.Student object at 0x000001A2B3C4D6F0>], with addresses that change every run.
Printing one student uses __str__. But printing a list shows each item using a different method, __repr__, and since this class doesn't have one, you get the memory addresses again.
__repr__ should return a precise, unambiguous description, ideally one that looks like the code to create the object:
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __str__(self):
return f"{self.name} (grade {self.grade})"
def __repr__(self):
return f"Student({self.name!r}, {self.grade})"
team = [Student("Maria", 11), Student("Ben", 10)]
print(team)
print(team[0])[Student('Maria', 11), Student('Ben', 10)]
Maria (grade 11)!r inside the f-string uses the value's own repr, which puts quotes around strings. You've seen this difference before: in the interactive shell from How Python Runs, typing a string shows it with quotes. That's repr. print shows it without them. That's str.
A good rule: always write __repr__, since it helps you debug, and add __str__ when users need a friendlier version. If a class has only __repr__, Python uses it for printing too.
__eq__: when are two objects equal?
By default, == between two objects only says True if they're the very same object, not two objects with the same data:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
print(Point(3, 4) == Point(3, 4))FalseDefine __eq__ to say what "equal" means for your class. It receives the other object and returns True or False:
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
print(Point(3, 4) == Point(3, 4))
print(Point(3, 4) == Point(4, 3))True
FalseNow ==, !=, and in on a list of points all work the way you'd expect.
__lt__: sorting your objects
sorted() and max() need to know which of two objects comes first. Define __lt__ (short for "less than"), which answers "is self less than other?":
class Student:
def __init__(self, name, average):
self.name = name
self.average = average
def __lt__(self, other):
return self.average < other.average
def __repr__(self):
return f"{self.name}: {self.average}"
students = [Student("Maria", 88.3), Student("Ben", 81.0), Student("Carlo", 94.3)]
print(sorted(students))
print(max(students))[Ben: 81.0, Maria: 88.3, Carlo: 94.3]
Carlo: 94.3__len__: working with len()
If your object holds a collection of things, __len__ lets len() count them:
class Team:
def __init__(self, name):
self.name = name
self.members = []
def add(self, person):
self.members.append(person)
def __len__(self):
return len(self.members)
chess = Team("Chess Club")
chess.add("Ana")
chess.add("Ben")
print(len(chess))2There are dozens more special methods, for everything from + (__add__) to looping with for (__iter__), but these are the ones you'll write most.
A shortcut: dataclasses
For classes that mostly just hold data, writing __init__, __repr__, and __eq__ by hand gets repetitive. Python's dataclasses module can write them for you:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
a = Point(3, 4)
print(a)
print(a == Point(3, 4))Point(x=3, y=4)
True@dataclass above the class is a decorator: it takes the class and adds methods to it. You list the attributes with type hints (from Parameters and Return Values), and it writes __init__, __repr__, and __eq__ automatically. Real projects use dataclasses constantly, so it's good to recognize them.
Try it
This program models a small class of students, and uses special methods to print them, compare them, sort them, and count them. Predict every line.
Running Python here
Running Python right in the browser is coming to Syntaxia soon. For now, you can read and edit the code here. If you have Python installed on your computer (see Setting Up), save it as main.py and run it with python main.py (or python3 main.py on macOS and Linux).
Check your prediction
Section 11-B, 3 students
[Student('Maria'), Student('Ben'), Student('Carlo')]
Top: Carlo (94.3)
Carlo (94.3)
Maria (88.3)
Ben (81.0)
TruePrinting the section uses its __str__, which calls len(self), which runs __len__. Printing the list shows each student's __repr__. max and sorted use __lt__ to compare averages. And the last line is True: __eq__ only compares names, so a brand-new Ben with no scores counts as "in" the list.
Try it yourself
- Remove the
__repr__method and run the program. What does the second line print now? - Change
__eq__so two students are equal only if their names and scores match. What does the last line print now? - Add an
__add__(self, other)method toSectionthat returns a new section containing both sections' students, sosection_a + section_bworks.
Check your understanding
Up next
That completes the Objects and Classes chapter. Your programs can now model real things. They can also go wrong in real ways, so the next chapter starts with the most useful skill of all: Debugging Python Programs.
