为什么 PyScripter 控制台的输出不同?

Why is the output different in PyScripter console?

来自 PyScripter (3.6.4.0) REPL 控制台:

*** Python 3.7.7 (tags/v3.7.7:d7c567b08f, Mar 10 2020, 10:41:24) [MSC v.1900 64 bit (AMD64)] on win32. ***
*** Remote Python engine is active ***
>>> d = {}
>>> d['B'] = 12
>>> d['A'] = 10
>>> d['C'] = 34
>>> d
{'A': 10, 'B': 12, 'C': 34}

这个结果让我们相信 Python 对键进行排序并且不保留插入顺序,而从版本 3.6 开始就保证了。

现在让我们在 PyScripter 之外的控制台中 运行 完全相同的 Python 版本:

Python 3.7.7 (tags/v3.7.7:d7c567b08f, Mar 10 2020, 10:41:24) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {}
>>> d['B'] = 12
>>> d['A'] = 10
>>> d['C'] = 34
>>> d
{'B': 12, 'A': 10, 'C': 34}

插入顺序全部保留。

为什么输出不同?

在 pyscripter REPL 中,当我不依赖 REPL 提供答案而是使用 print 语句时,我得到了正确的结果:

>>> print(d)
{'B': 12, 'A': 10, 'C': 34}

其中 d 生成的键就好像它们已排序一样:

>>> d
{'A': 10, 'B': 12, 'C': 34}

所以当涉及到字典顺序时,不要相信 pyscripter REPL 输出。

您需要在 pyscripter 中禁用 Pretty print output 选项:

If checked the standard python module pprint is used to format interpreter output.

选项 > IDE 选项 > Python 解释器.

下找到它

pprint module 以排序的键顺序输出字典,忽略字典的当前顺序。来自文档:

Dictionaries are sorted by key before the display is computed.

匹配旧的 Python 版本输出不是 hack,因为在 Python 3.6 之前 order depended on insertion and deletion order plus a randomised hash seed.

相反,当输出变得笨拙时,使用 pprint 可以提供更好的输出,方法是使用换行符和缩进,其中标准表示只是将所有内容放在一行中。

你的具体例子并没有真正说明区别,更长的字典会更清楚:

>>> from requests import get
>>> from secrets import token_hex
>>> from pprint import pprint
>>> fortunes = {token_hex(4): get("https://fortuneapi.herokuapp.com/").text for _ in range(5)}
>>> print(repr(fortunes))  # standard REPL output prints the repr() result
{'a33435f0': '"If reporters don\'t know that truth is plural, they ought to be lawyers.\n\t\t-- Tom Wicker\n"\n', '1f08db3c': '"Very few profundities can be expressed in less than 80 characters.\n"\n', '6037e01e': '"The main problem I have with cats is, they\'re not dogs.\n\t\t-- Kevin Cowherd\n"\n', 'b817eaf8': '"New York now leads the world\'s great cities in the number of people around\nwhom you shouldn\'t make a sudden move.\n\t\t-- David Letterman\n"\n', 'c89994e7': '"I\'m GLAD I remembered to XEROX all my UNDERSHIRTS!!\n"\n'}
>>> pprint(fortunes)  # pprint outputs pretty-printed lines, sorted.
{'1f08db3c': '"Very few profundities can be expressed in less than 80 '
             'characters.\n"\n',
 '6037e01e': '"The main problem I have with cats is, they\'re not '
             'dogs.\n\t\t-- Kevin Cowherd\n"\n',
 'a33435f0': '"If reporters don\'t know that truth is plural, they ought to be '
             'lawyers.\n\t\t-- Tom Wicker\n"\n',
 'b817eaf8': '"New York now leads the world\'s great cities in the number of '
             "people around\nwhom you shouldn't make a sudden "
             'move.\n\t\t-- David Letterman\n"\n',
 'c89994e7': '"I\'m GLAD I remembered to XEROX all my UNDERSHIRTS!!\n"\n'}

如果您只是偶尔需要检查字典的特定项目顺序,您可以只启用该选项,并在那些特殊时间使用 print(dictionary)

您也可以将 sort_dicts=False 参数传递给 pprint(),前提是您手动调用它; pprint.pp() function 甚至将其设为默认值:

>>> from pprint import pp
>>> pp(fortunes)
{'a33435f0': '"If reporters don\'t know that truth is plural, they ought to be '
             'lawyers.\n\t\t-- Tom Wicker\n"\n',
 '1f08db3c': '"Very few profundities can be expressed in less than 80 '
             'characters.\n"\n',
 '6037e01e': '"The main problem I have with cats is, they\'re not '
             'dogs.\n\t\t-- Kevin Cowherd\n"\n',
 'b817eaf8': '"New York now leads the world\'s great cities in the number of '
             "people around\nwhom you shouldn't make a sudden "
             'move.\n\t\t-- David Letterman\n"\n',
 'c89994e7': '"I\'m GLAD I remembered to XEROX all my UNDERSHIRTS!!\n"\n'
}

或者您可以要求 PyScripter 项目在其控制台实现中使用该选项。