为什么 **kwargs 映射与不同顺序的 OrderedDict 比较相等?

Why does the **kwargs mapping compare equal with a differently ordered OrderedDict?

根据PEP 468

Starting in version 3.6 Python will preserve the order of keyword arguments as passed to a function. To accomplish this the collected kwargs will now be an ordered mapping. Note that this does not necessarily mean OrderedDict.

在那种情况下,为什么此有序映射无法遵守与 Python 的规范有序映射类型 collections.OrderedDict:

的相等性比较
>>> from collections import OrderedDict
>>> data = OrderedDict(zip('xy', 'xy'))
>>> def foo(**kwargs):
...     return kwargs == data
... 
>>> foo(x='x', y='y')  # expected result: True
True
>>> foo(y='y', x='x')  # expected result: False
True

虽然现在保留了迭代顺序,但 kwargs 似乎在比较时表现得像一个普通的字典。 Python 有一个 C 实现的有序字典 since 3.5,因此可以想象它可以直接使用(或者,如果性能仍然是一个问题,使用 3.6 紧凑字典的精简子类的更快实现)。

为什么函数接收到的有序映射不遵循相等比较中的顺序?

不管“有序映射”是什么意思,只要它不一定是OrderedDictOrderedDict==就不会考虑它的顺序。 Docs:

Equality tests between OrderedDict objects are order-sensitive and are implemented as list(od1.items())==list(od2.items()). Equality tests between OrderedDict objects and other Mapping objects are order-insensitive like regular dictionaries. This allows OrderedDict objects to be substituted anywhere a regular dictionary is used.

只是补充一下,如果您确实想进行此检查(不依赖于实现细节(即便如此,也不会出现在 python 3.7 中)),只需

from collections import OrderedDict
>>> data = OrderedDict(zip('xy', 'xy'))
>>> def foo(**kwargs):
...     return OrderedDict(kwargs) == data

因为这保证是真的。

"Ordered mapping" 仅表示映射必须保持顺序。这并不意味着顺序必须是映射的 == 关系的一部分。

PEP 468 的目的只是为了保留订购信息。让订单成为 == 的一部分会产生向后不兼容,而对任何激发 PEP 468 的用例没有任何实际好处。使用 OrderedDict 也会更昂贵(因为 OrderedDict 仍然保持其拥有单独的链表来跟踪顺序,并且它不能在不牺牲 popitemmove_to_end 中的 big-O 效率的情况下放弃该链表。

第一个 'why' 的答案是因为此功能是通过使用 CPython 中的普通 dict 实现的。正如@Ryan 的回答所指出的,这意味着比较不会是 order-sensitive。

此处的第二个 'why' 是不使用 OrderedDict 的原因。

使用 OrderedDict 是该线程上 first draft of PEP 486. The idea, as stated in this reply, was to collect some perf data to show the effect of plugging in the OrderedDict since this was a point of contention when the idea was floated around before. The author of the PEP even alluded to the order preserving dict being another option in the final reply 中所述的初始计划。

在那之后,关于这个话题的讨论似乎已经平息,直到 Python 3.6 出现。当新的 dict 出现时,它有很好的 side-effect 开箱即用地实现 PEP 486(因为 this Python-dev thread states). The specific message in that thread also states how the author wanted the term OrderedDict to be changed to Ordered Mapping. (This is also when a new commit on PEP 468,在最初的一个之后)

据我所知,进行此改写是为了允许其他实现提供他们认为合适的此功能。 CPython 和 PyPy 已经有一个可以轻松实现 PEP 468 的字典,其他实现可能会选择 OrderedDict,其他实现可能会选择另一种形式的有序映射。

不过,这确实为问题打开了大门。它确实意味着,理论上,在 Python 3.6 的实现中,使用 OrderedDict 作为实现此功能的结构,比较将是 order-sensitive 而在其他情况下(CPython ) 它不会。 (在 Python 3.7 中,所有 dict 都必须是 insertion-ordered 所以这一点可能没有实际意义,因为所有实现都会将它用于 **kwargs

虽然这看起来确实是个问题,但实际上并非如此。正如@user2357112 所指出的,== 并不能保证。 PEP 468 保证顺序。据我所知,== 基本上是实现定义的。


简而言之,它比较等于 in CPython 因为 kwargs in CPython 是一个 dict 并且它是dict 因为在 3.6 之后整个事情就开始了。