Python __str__ - can it be ambiguous?

Viewed 75

Can __str__ return literally anything my soul desires?

The documentation on __str__ states:

[...] the “informal” or nicely printable string representation of an object

How should one interpret it?

  • A. That it can return anything as long as in my opinion it represents the object.
  • B. That it should return unambiguous representation of the object, albeit it can be formatted in any way I please.

Example of A:

class Vehicle:
    def __init__(self, owner):
        self.owner = owner
        self.wheels = 4
    def __str__(self):
        return "%s with %s wheels" % (self.owner, self.wheels)
print(Vehicle('Benjamin'))
>>> Benjamin with 4 wheels

Example of B:

class Vehicle:
    def __init__(self, owner):
        self.owner = owner
        self.wheels = 4
    def __str__(self):
        return "Vehicle, owner is %s, with %s wheels" % (self.owner, self.wheels)
print(Vehicle('Benjamin'))
>>> Vehicle, owner is Benjamin, with 4 wheels

Just about all resources I find (1, 2, 3, 4, 5, 6) suggest that the output - while it can be informal - should be unambiguous and explicit (and it makes sense: Explicit is better than implicit., Readability counts. In the face of ambiguity, refuse the temptation to guess.).

However there are a few that suggest that one could in fact be ambiguous and implicit (1, 2) given the 'informal' word in the documentation. I found one fellow developer who states that one can use absolutely any representation of the object in __str__, and that __repr__ is for unambiguous and explicit representations. I'd like to understand their perspective but can't find any reasoning supporting their case. Can you suggest how one should interpret that documentation and support your reasoning?

1 Answers

The question is how these things are used. As an example of how __str__ can be used, look at Django's admin interface. Simply by registering one of your model classes, it generates a full featured admin interface around it. By default, in a list of such model objects, the objects are represented using their __str__ representation (this can be customised further, but this is the sensible default). So, the __str__ should return a representation that makes sense in an admin interface. It shouldn't be too verbose, it shouldn't be a sentence, but it should uniquely identify the object.

So how to uniquely identify the object in a useful way depends on what it is exactly and your app. Let's say you have a blog app, then for identifying each article in it, something like this might be most useful:

def __str__(self):
    return f'{self.id} — {self.title}'

It's really up to you and your use case, but mostly it should be something useful for a human. There's no programatic use for __str__ representations, so Python doesn't particularly care nor dictate any specific format it must adhere to.

__repr__ should, if at all possible, return a literal which allows you to reconstruct the exact object. This is mostly for the developer, and can greatly aid debugging. If you do something like print(things) and get this:

[<__main__.Thing object at 0x818745692312>]

That only gives you minimal information and not much more. If you customise __repr__:

def __repr__(self):
    return f'{type(self).__name__}(foo={self.foo!r})'

…and your list now prints like this:

[Thing(foo='bar')]

Now you can copy-paste this directly into a Python shell and play around with it further to reproduce a problem and debug it further. That's what it's for, and using it properly can simplify your development.

Related