为什么Python的函数调用语义传入关键字参数不是有序的?

Why Python’s function call semantics pass-in keyword arguments are not ordered?

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

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

我知道我们可以使用:

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

但它不如:

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

[编辑1 ]:

1)我认为有两种情况:

  • 我需要知道顺序,用户可以通过文档了解这种行为。
  • 我不需要这个订单,所以我不在乎它是否被订购。

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

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

因为他不知道听写是不受命令的(即使他应该知道)

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

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

[编辑2 ]:

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


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

这是词典的定义。

CPython implementation detail: Keys and values are listed in an
arbitrary order which is non-random, varies across Python
implementations, and depends on the dictionary’s history of insertions
and deletions.

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

python的字典是整个语言工作方式的核心,因此它们是高度优化的。添加排序将影响性能,并需要更多的存储和处理开销。

你可能会遇到一个不真实的情况,但我认为这比普通情况更为特殊。为一个非常热的代码路径添加一个"以防万一"的特性不是一个明智的设计决策。

编辑:

只是FYI

1
2
3
4
5
>>> 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),特别适用于地球物理数据处理。

我在这里发布了一个有例子的问题:

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


如果函数的参数是如此相关以至于名称和顺序都很重要,那么考虑使用特定的数据结构或定义一个类来保存它们。很有可能,您希望它们在代码的其他地方放在一起,并可能定义使用它们的其他函数/方法。