为什么Python的函数调用语义传入关键字参数没有排序



使用函数定义中的双星语法,我们获得了一个正则字典。问题是它丢失了用户输入顺序。有时,我们可能想知道关键字参数传递给函数的顺序。

由于通常函数调用不涉及太多参数,我不认为这是性能问题,所以我想知道为什么默认情况下不维护顺序。

我知道我们可以使用:

from collections import Ordereddict
def my_func(kwargs):
    print kwargs
my_func(Ordereddict(a=1, b=42))

但它没有那么简洁

def my_func(**kwargs):
    print kwargs
my_func(a=1, b=42)

[编辑1]:

1) 我认为有2个案例:

  • 我需要知道订单,这种行为是用户通过文档知道的
  • 我不需要订单,所以我不在乎是否订购

我没有想到,即使用户知道它使用订单,他也可以使用:

a = dict(a=1, b=42)
my_func(**a)

因为他不知道dict没有被命令(即使他应该知道)

2) 我认为,在少数争论的情况下,开销不会很大,因此,有一种新的可能性来管理争论的好处将优于这种缺点。

但似乎(从乔的回答来看)开销是不可忽略的。

[编辑2]:

似乎PEP 0468——在函数中保持**kwargs的顺序正朝着这个方向发展。

因为字典不是按定义排序的。我觉得事情就这么简单。kwargs的目的是精确地处理那些没有排序的形式参数。如果您确实知道订单,那么您可以将它们作为"正常"参数或*args接收。

这是字典里的定义。

CPython实现细节:键和值列在非随机的任意顺序,在Python中各不相同实现,并且取决于字典的插入历史记录以及删除。

http://docs.python.org/2/library/stdtypes.html#dict

Python的字典是整个语言工作方式的核心,因此它们经过了高度优化。添加订购会影响性能,并需要更多的存储和处理开销。

你可能会遇到这样的情况,但我认为这比常见情况更为特殊。为一个非常热门的代码路径添加一个功能"以防万一"不是一个明智的设计决定。

编辑:

仅供参考

>>> timeit.timeit(stmt="z = dict(x)", setup='x = ((("one", "two"), ("three", "four"), ("five", "six")))', number=1000000)
1.6569631099700928
>>> timeit.timeit(stmt="z = OrderedDict(x)", setup='from collections import OrderedDict; x = ((("one", "two"), ("three", "four"), ("five", "six")))', number=1000000)
31.618864059448242

这大约是构建一个"正常"大小的小字典的30倍速度差。OrderedDict是标准库的一部分,所以我不认为它会有更多的性能被挤出。

作为一个反参数,这里有一个例子说明了这将导致的复杂语义。这里有几个案例:

  • 函数总是得到一个无序的字典
  • 函数总是得到一个有序的字典——鉴于此,我们不知道顺序是否有任何意义,就像用户传入一个无序的数据结构一样,顺序是任意的,而数据类型意味着顺序
  • 函数获取传入的任何内容——这看起来很理想,但并没有那么简单

some_func(a=1, b=2, **unordered_dict)的情况如何?原始关键字参数中存在隐式排序,但dict是无序的。在订购与否之间没有明确的选择。

考虑到这一点,我认为对关键字参数进行排序是没有用的,因为不可能判断顺序是否只是任意的。这将模糊函数调用的语义。

考虑到这一点,将其作为调用的一部分所获得的任何好处都将丢失——相反,只需期待一个OrderedDict作为参数即可。

如果函数的参数非常相关,以至于名称和顺序都很重要,请考虑使用特定的数据结构或定义一个类来保存它们。很可能,您会希望它们在代码中的其他位置结合在一起,并可能定义使用它们的其他函数/方法。

检索通过**kwargs传递的关键字参数的顺序在我正在进行的特定项目中非常有用。这是关于制作一种具有有意义维度的n-d numpy数组(现在称为dimarray),对地球物理数据处理特别有用。

我在这里发布了一个带有示例的开发问题:

如何检索传递给函数调用的关键字参数的原始顺序?

最新更新