pythonpython-3.xdictionarysetpprint

pprint sorting dicts but not sets?


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.


Solution

  • 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.