__str__ vs. __repr__

Tim Peters tim_one at email.msn.com
Wed Nov 3 00:08:56 EST 1999


[Randall Hopper wonders about str-vs-repr, Tim explains that lists ask
 their elements to produce repr() even if the list itself was passed
 to str()
]

[Guido]
> Actually, Python's internals *do* follow the convention.  Dicts and
> lists don't define __str__(), so str() for them defaults to
> __repr__(), and then of course repr() is used for the items.  It may
> be confusing, it may not be what you want, but it *is* consistent ;-(

Not to mention undocumented <wink>.

[Randall]
> For consistency, would it make sense to change this for Python 1.5.3 (that
> is, have sequence and dict types pass 'str-vs-repr'ness down)?

[Guido]
> This has been asked a few times before (and usually it's been reported
> as a bug, which it isn't -- see above).  I happened to see this post
> and it made me delve deep into my intuition trying to figure out why I
> don't like propagating str() down container items.
>
> Here's what I think it is.  There's no reason why an object's str()
> should be particularly suited to being included in list syntax.

This seems much more a consequence of the current design than an argument in
favor of it.  That is, had Python been designed so that the builtin
container types did "pass down" str-vs-repr'ness, an object's str() would
have every reason to produce a string suited to being etc.

> For example, I could have a list containing the following items:
>
> 	1		# an integer
> 	'1'		# a string (of an integer literal)
> 	'2, 3'		# a string containing a comma and a space
> 	'], ['		# a string containing list delimiters
>
> Under the proposed rules, this list would print as:
>
> 	[1, 1, 2, 3], []
>
> I would find this confusing

Me too, but I find the current design more rigidly consistent than useful
(see below).  In a world where containers passed str() down, a container's
str() would presumably be responsible for adding disambiguating delimeters
to element str() results when needed (the container knows its own output
syntax, and can examine the strings produced by its elements -- not rigidly
consistent, but useful <wink>).

> and I worry that it could be used to fool the user.

People can already define __repr__ to return anything whatsoever; the
reports of people getting fooled by this are conspicuous by absence <wink>.

Here's something wholly typical of what I dislike:

>>> from Rational import Rat, Format
>>> Rat.set_df(Format.Format(mode=Format.FIXED, prec=3, use_tag=0))
Format(mode=Format.MIXED, prec=8, base=10, round=Round.NEAREST_EVEN,
       use_tag=1, use_letters=1)
>>> one_tenth = Rat.Rat(.1)
>>> one_tenth
Rat(3602879701896397L, 36028797018963968L)
>>> print one_tenth
0.100
>>>

That is, in interactive mode, I'm forever using "print" because the default
of applying repr() to raw expressions produces the output least useful in
interactive hacking (I don't care about reproducing the object exactly from
the string when typing a raw expression at the prompt!  The mental ratio of
two giant integers isn't helpful here.).

Carry it one more step, and nothing simple suffices anymore:

>>> values = [one_tenth, one_tenth + 100]
>>> values
[Rat(3602879701896397L, 36028797018963968L),
 Rat(3606482581598293197L, 36028797018963968L)]
>>> print values
[Rat(3602879701896397L, 36028797018963968L),
 Rat(3606482581598293197L, 36028797018963968L)]
>>>

So I'm forever typing this instead:

>>> map(str, values)
['0.100', '100.100']
>>>

Throw a dict into it, and it's hopeless:

>>> recip = {one_tenth: 1/one_tenth, 1/one_tenth: one_tenth}
>>> print recip
{Rat(3602879701896397L, 36028797018963968L):
     Rat(36028797018963968L, 3602879701896397L),
 Rat(36028797018963968L, 3602879701896397L):
     Rat(3602879701896397L, 36028797018963968L)}
>>>

Having gone thru the same business in dozens of classes over the years, I
find the current design simply unusable in interactive mode.  For a while I
defined just __str__, bound __repr__ to that too, and added a .repr()
*method* for the unusual cases in which I really needed a faithful string.
But that frustrated other code that expected explict repr() calls, and/or
the `` notation, to produce the long-winded version.  So that sucked too.

It's even an irritation sticking to builtin types; e.g., here assuming
Latin-1 comes across intact:

>>> names = ["François", "Tim"]
>>> print names[0]
François
>>> >>> print names
['Fran\347ois', 'Tim']
>>>

That isn't helpful either -- it's frustrating.

in-the-face-of-ambiguity-refuse-the-temptation-to-do-the-opposite-of-
    what-the-user-asked-for<0.9-wink>-ly y'rs  - tim






More information about the Python-list mailing list