Sign in to your Python Morsels account to save your screencast settings.
Don't have an account yet? Sign up here.
Let's talk about how to customize the string representations of your Python objects.
We have a Point
class here:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
We've made a new instance of this class and we're referring to that instance with the variable p
.
If we type p
from the Python REPL, we see that it's a point.Point
object at some memory location:
>>> p = Point(1, 2)
>>> p
<point.Point object at 0x7f128f519ee0>
If we print it we see the same thing:
>>> print(p)
<point.Point object at 0x7f128f519ee0>
And if we convert to a string explicitly, we see the same thing:
>>> str(p)
'<point.Point object at 0x7f128f519ee0>'
__str__
It would be nice to see something, besides the module name and the class name of this object.
Maybe the data that's actually stored in this object.
Let's create a __str__
method on this Point
class (pronounced "dunder str
", dunder meaning "double underscore"):
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
This will customize what happens when we pass the Point
object to the built-in str
function:
>>> p = Point(1, 2)
>>> str(p)
'(1, 2)'
>>> p.__str__()
'(1, 2)'
The built-in str
function actually calls the __str__
method on the object that we give it.
In fact, if we print something out or otherwise, implicitly convert something to a string, it does the same thing: it calls that __str__
method.
>>> print(p)
(1, 2)
__repr__
We're not done yet! Our Python object does not yet have a nice string representation in all cases.
If we type p
from the Python REPL, we still see the point.Point
object at some memory location:
>>> p
<point.Point object at 0x7f7e54f46be0>
The REPL actually doesn't use the str
built-in function, it uses the built-in repr
function.
>>> repr(p)
'<point.Point object at 0x7f7e54f46be0>'
And the built-in repr
function relies on the __repr__
method:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
By making a __repr__
method, we've customized what happens when you call repr
on our Point
objects:
>>> p = Point(1, 2)
>>> repr(p)
'Point(1, 2)'
And by making a __str__
method, we've customized what happens when you call str
:
>>> str(p)
'(1, 2)'
And so when we just type p
or when we print(p)
, we'll get friendly string representations:
>>> p
Point(1, 2)
>>> print(p)
(1, 2)
So, we've customized the programmer-readable string representation for the object (which by convention looks like Python code) and a human-readable string representation for our object, which is what an end-user might expect to see.
__repr__
Almost every Python object only customizes one of these: __repr__
, the programmer readable string representation.
If we just define a __repr__
method on our class:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
It will customize what programmers see:
>>> p = Point(1, 2)
>>> p
Point(1, 2)
But it also customizes what happens when we print out our object:
>>> print(p)
Point(1, 2)
On when we convert it to a string explicitly:
>>> str(p)
'Point(1, 2)'
This happens because the default __str__
method on all Python objects delegates to the __repr__
method.
>>> p.__str__()
'Point(1, 2)'
When you're defining your own classes in Python, always make a __repr__
method.
If you want an additional human-readable string representation, you might wanna customize the __str__
method, but you at least need to customize the __repr__
method of your Python objects.
See Python's 2 different string representations for more on str
and repr
.
Need to fill-in gaps in your Python skills?
Sign up for my Python newsletter where I share one of my favorite Python tips every week.
Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.