[Python-ideas] Fwd: Re: Unambiguous repr for recursive objects

Steven D'Aprano steve at pearwood.info
Sun Dec 27 19:55:09 EST 2015


On Sun, Dec 27, 2015 at 10:16:34AM -0700, Guido van Rossum wrote:

> I really feel you all are overworrying and overthinking this. A downside to
> me is that <...> isn't clear about what the type of the object is. The use
> case here is not sophisticated users, it's beginners who have accidentally
> managed to create a recursive list or dict. They have most likely not even
> encountered Ellipsis objects yet. There's nothing clearer than the current
> notation to help them see that they've done something unusual.

As a data point, or perhaps an anecdote point, I've been a regular on 
the tutor@ and python-list@ lists for many years now, and I don't recall 
seeing recursive lists being an issue. I can't categorically say that it 
has *never* come up, but it certainly isn't common.

My sense is that the not-really-an-invariant-more-of-a-guideline that 
eval'ing the repr of an object returns the object is not that important 
here. There are many things you can put in a list which will break the 
invariant. It is a little unfortunate that [...] is no longer a syntax 
error, giving us this:

eval("[[...]]") == [[Ellipsis]]

but I don't see that as a problem worth fixing.

I think the repr of OrderedDict is fine the way it is, and I like 
the fact that it uses a bare ... to refer to itself rather than 
wrapping it in braces like regular dicts. It just looks nicer in the 
OrderedDict repr:

OrderedDict([('key', ...)])

versus

OrderedDict([('key', {...})])


I thought I would generate an extreme example, an OrderedDict with 
multiple references to itself in values which contain references to 
themselves as well:

py> from collections import OrderedDict
py> o = OrderedDict([(1, []), (2, {}), (3, ('a', []))])
py> o[1].append(o[1])
py> o[1].append(o)
py> o[2]['x'] = o[2]
py> o[2]['y'] = o
py> o[3][-1].append(o[3])
py> o[3][-1].append(o)
py> o[4] = o
py> o
OrderedDict([(1, [[...], ...]), (2, {'y': ..., 'x': {...}}), (3, ('a', 
[(...), ...])), (4, ...)])


As an extreme case, I would hope that I would never need to debug 
something this complex in real life, but I think it is useful to see all 
the different kinds of recursive reprs in one place. I think it is 
useful that they are all slightly different. If it looked like this:

OrderedDict([(1, [<...>, <...>]), (2, {'y': <...>, 'x': <...>}), 
(3, ('a', [<...>, <...>])), (4, <...>)])


we would lose valuable hints about the types, and if they all used 
object __repr__ the amount of visual noise would be overwhelming:

OrderedDict([(1, [<list object at 0xb7bcf7cc>, <collections.OrderedDict 
object at 0xb7bcbc5c>]), (2, {'y': <collections.OrderedDict object at 
0xb7bcbc5c>, 'x': <dict object at 0xb7c6f96c>}), (3, ('a', [<tuple 
object at 0xb7bb320c>, <collections.OrderedDict object at 
0xb7bcbc5c>])), (4, <collections.OrderedDict object at 0xb7bcbc5c>)])


Given the risk that any such change will break doctests, I don't think 
this is a problem worth fixing:

+1 on keeping the status quo
-1 on using the verbose object.__repr__
-0.5 on consistently using <...> for all types
-0.5 on changing the repr of recursive OrderedDicts to be more like dict



-- 
Steve


More information about the Python-ideas mailing list