代码之家  ›  专栏  ›  技术社区  ›  wim

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

  •  12
  • wim  · 技术社区  · 7 年前

    根据 PEP 468 :

    从3.6版开始,Python将保留传递给函数的关键字参数的顺序。为此,收集 kwargs现在将是一个有序映射 。请注意,这并不一定意味着 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实现的有序dict since 3.5 ,因此可以想象它可以直接使用(或者,如果性能仍然是一个问题,则可以使用3.6 compact dict的一个精简子类实现更快的实现)。

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

    4 回复  |  直到 7 年前
        1
  •  14
  •   Ry- Vincenzo Alcamo    7 年前

    不管有序映射意味着什么,只要它不一定 OrderedDict ,则, 订购的DICT s == 不会考虑它的顺序。 Docs:

    之间的相等性测试 订购的DICT 对象是顺序敏感的,并且实现为 list(od1.items())==list(od2.items()) 之间的相等性测试 订购的DICT 对象和其他 Mapping 对象与常规词典一样不区分顺序。 这允许 订购的DICT 要在使用常规词典的任何位置替换的对象。

        2
  •  10
  •   user2357112    7 年前

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

    PEP 468的目的只是保留订购信息。让订单成为 == 将产生向后的不兼容性,而不会给任何激发PEP 468的用例带来任何真正的好处。使用 OrderedDict 也会更贵(因为 订购的DICT 仍然维护自己的单独链表来跟踪顺序,并且它不能在不牺牲big-O效率的情况下放弃该链表 popitem move_to_end )。

        3
  •  8
  •   Dimitris Fasarakis Hilliard    6 年前

    第一个“为什么”的答案是因为此功能是通过使用 dict 在CPython。正如@Ryan的回答所指出的,这意味着比较不会对顺序敏感。

    第二个“为什么”是为什么不使用 OrderedDict

    使用 订购的DICT 初始计划是否如 first draft 政治公众人物486。如前所述 in this reply ,是为了收集一些性能数据,以显示在 订购的DICT 因为这是一个争论点,当这个想法之前被提出的时候。政治公众人物(PEP)的作者甚至暗示,维持秩序的格言是 final reply 在那条线上。

    此后,在Python 3.6出现之前,关于这个主题的对话似乎已经停止了。当新的dict出现时,它有一个很好的副作用,就是直接实现PEP 486(如 this Python-dev thread states )。该线程中的特定消息还说明了作者希望使用该术语的方式 订购的DICT 要更改为有序映射。(这也是 new commit on PEP 468 ,在最初的一个之后)

    据我所知,这次重写是为了允许其他实现在他们认为合适的时候提供此功能。CPython和PyPy已经有了一个可以轻松实现PEP 468的dict,其他实现可能会选择 订购的DICT ,其他人可以使用另一种形式的有序映射。

    不过,这确实为一个问题打开了大门。理论上,这确实意味着在Python 3.6的实现中 订购的DICT 作为实现此功能的结构,比较将是顺序敏感的,而在其他(CPython)中则不是。(在Python 3.7中,所有 字典 s需要按插入顺序排列,因此这一点可能没有实际意义,因为所有实现都会将其用于 **kwargs )

    虽然这看起来确实是一个问题,但实际上并不是。正如@user2357112指出的,不能保证 == 。政治公众人物468 只有 保证订单。据我所知, == 基本上是实现定义的。


    简言之,它与 在CPython 因为 kwargs 在CPython是 字典 这是一个 字典 因为之后 3.6 整件事都成功了。

        4
  •  1
  •   FHTMitchell    7 年前

    要补充的是,如果您确实想进行此检查(不依赖于实现细节(即使这样,也不会在python 3.7中),只需执行

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

    因为这肯定是真的。