pprint sorting dicts but not sets?
Asked Answered
M

1

13

I know that dicts and sets aren't ordered, so equal sets or dicts may print differently (all tests with Python 3.6.1):

>>> for obj in {0, 8}, {8, 0}, {0:0, 8:8}, {8:8, 0:0}:
        print(obj)

{0, 8}
{8, 0}
{0: 0, 8: 8}
{8: 8, 0: 0}

And I just realized that pprint (“pretty-print”) sorts dicts but not sets:

>>> for obj in {0, 8}, {8, 0}, {0:0, 8:8}, {8:8, 0:0}:
        pprint.pprint(obj)

{0, 8}
{8, 0}
{0: 0, 8: 8}
{0: 0, 8: 8}

It's documentation also says "Dictionaries are sorted by key before the display is computed". But why doesn't it also sort sets? Doesn't seem pretty to me. And is there a way to make it sort sets? Also inside nested structures, as that's a main purpose of pprint.

Margherita answered 10/7, 2017 at 15:48 Comment(1)
I guess it doesn't support sets, but you could subclass pprint.PrettyPrinter and override pformat to handle sets if you wanted...Smokechaser
D
6

This was raised in issue 27495 and it is a bug, rather than just a design choice, but apparently has not yet been resolved.

Here is another example from the issue that illustrates perhaps more obviously the behavior you identify in Python 3:

>>> import string, pprint
>>> pprint.pprint(set(string.digits))
{'7', '1', '9', '8', '3', '0', '2', '5', '6', '4'}

The same applies for frozenset() too, but note that multi-line pprint outputs are sorted in Python 3, for example:

>>> pprint.pprint(set(string.digits), width=1)
{'0',
 '1',
 '2',
 '3',
 '4',
 '5',
 '6',
 '7',
 '8',
 '9'}

However, in Python 2, the output from the same original code is sorted:

>>> pprint.pprint(set(string.digits))
set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])

I think it is the inconsistency between Python 3 and Python 2, and between the single-line multi-line behavior, that makes this a bug.

For dicts, a similar example, illustrates as you note, that the output is sorted in either Python 3 or 2, as it should be:

>>> pprint.pprint({i:None for i in set(string.digits)})
{'0': None,
 '1': None,
 '2': None,
 '3': None,
 '4': None,
 '5': None,
 '6': None,
 '7': None,
 '8': None,
 '9': None}

However, for Python 3.6, it could be considered surprising that pprint sorts dicts since they are ordered now. However, since this is just an implementation detail (for now) I guess there is no obligation for pprint to maintain the insertion order (yet), and doing so would break pprint's own consistency across Python versions of always sorting dicts.

Darra answered 11/7, 2017 at 8:7 Comment(11)
No, they are not ordered now, that is a implementation detail of cPython, other implementations can implement dicts as unordered and still be conforming to the spec, that only mandates that mappings used in some cases (PEP468, PEP520) are order preserving. A different implementation could choose to handle those cases specially and keep an unordered dict implementation. Until this is officially part of the specification, dicts should not be treated as order preserving, and even then doing so would destroy backward compatibility.Ryley
Thanks. Maybe I should've looked for and found that issue report myself. Really strange how it sorts some things but not others. Good point about ordered dicts. If that does become guaranteed, I guess pprint really must not sort them anymore, which would be somewhat sad because less pretty.Margherita
But the way pprint works isn't an implementation detail, that's what makes this a bug in the first place. pprint can only rely on the existing dict order if that is a defined in the language specification. As long as it isn't, the options would only be to sort, not to sort or to explicitly check if we're on cPython >= 3.6.Ryley
@Ryley I think we agree, I was just making an observation, I know you can't rely on an implementation detail. Incidentally, it is likely to become language spec eventually twitter.com/raymondh/status/850102884972675072Darra
Or... hmm... maybe they'll just make OrderedDict be exactly the same as dict except with an order guarantee (instead of an extra implementation like it is now). I mean, as far as I understand, the order in the new dict implementation is a side effect, and the actual goal was to make it more efficient. Maybe someone will come up with an even more efficient dict implementation that doesn't keep order. And then with an order guarantee, they couldn't use it (without breaking things). So there's reason to not guarantee it.Margherita
@StefanPochmann no, those tweets talk about making insertion order part of the language spec. How well that will go will probably depend on how much a burden that would put on other implementations who currently do something different like IronPython, micropython, ...Ryley
@StefanPochmann Maybe, I think we would be nice to have a guarantee either way in the future, currently it is not guaranteed they're ordered and not guaranteed they're un-orderedDarra
@Darra I think it's perfectly appropriate that set and dict have no order guarantee. They're mathematical concepts (and Python is a rather mathy language) where they simply don't have order.Margherita
@StefanPochmann That 'no' was pointed at the first sentence of your previous comment. I'm used in answering in quote/interleaved style where this wouldn't be an issue :)Ryley
@Ryley Ah, so you just meant that in your humble opinion, they won't do what I thought they might do. It sounds like you were saying they won't do that, which you can't know and shouldn't assert.Margherita
Note that as of Python 3.7 insertion order is a language feature, as per the updated accepted answer for the question you linked to.Copperas

© 2022 - 2025 — McMap. All rights reserved.