是否可以将OrderedDict实例传递给使用**kwargs
语法并保留排序的函数?
我想做的是:
def I_crave_order(**kwargs):
for k, v in kwargs.items():
print k, v
example = OrderedDict([('first', 1), ('second', 2), ('third', -1)])
I_crave_order(**example)
>> first 1
>> second 2
>> third -1
然而实际结果是:
>> second 2
>> third -1
>> first 1
即典型的随机字典排序。
我有其他用途,明确设置顺序是好的,所以我想保留**kwargs
而不只是将OrderedDict作为常规参数传递
答案 0 :(得分:28)
从Python 3.6开始,the keyword argument order is preserved。在3.6之前,由于OrderedDict
变为dict
,因此无法实现。
首先要注意的是,您在**example
中传递的值不会自动成为**kwargs
中的值。考虑这种情况,其中kwargs
只包含example
的一部分:
def f(a, **kwargs):
pass
example = {'a': 1, 'b': 2}
f(**example)
或者这种情况下,它的值将超过示例中的值:
example = {'b': 2}
f(a=1, c=3, **example)
甚至根本没有重叠:
example = {'a': 1}
f(b=2, **example)
所以,你所要求的并没有多大意义。
但是,如果有某种方式可以指定您想要一个有序的**kwargs
,那么无论哪里关键字来自 - 显式关键字args按照它们出现的顺序,然后是**example
按example
中出现的顺序排列的所有项目(如果example
是dict
,这可能是任意的,但如果它OrderedDict
也可能有意义是OrderedDict
。
定义所有繁琐的细节,并保持性能可接受,结果证明比听起来更难。有关该想法的一些讨论,请参阅PEP 468和链接的线程。这次似乎已经停滞不前了,但如果有人拿起它并支持它(并为人们编写参考实现 - 这取决于可从C API访问的OrderedDict
,但希望是在3.5+),我怀疑它最终会进入语言。
顺便说一句,请注意,如果 可能,它几乎肯定会在>>> d = OrderedDict(a=1, b=2, c=3)
OrderedDict([('a', 1), ('c', 3), ('b', 2)])
本身的构造函数中使用。但是,如果你尝试这样做,你所做的就是冻结一些任意顺序作为永久订单:
example
同时,你有什么选择?
嗯,显而易见的选择就是将def f(example):
pass
example = OrderedDict([('a', 1), ('b', 2)])
f(example)
作为普通参数传递而不是解压缩它:
*args
或者,当然,你可以使用{{1}}并将这些项目作为元组传递,但这通常是更加丑陋。
从PEP链接的线程中可能还有其他一些解决方法,但实际上,它们都不会比这更好。 (除了...... IIRC,李浩义提出了一个基于他的MacroPy的解决方案来传递保留关键字的参数,但我不记得细节。如果你愿意使用MacroPy解决方案一般都很棒MacroPy并编写不像Python那样读取的代码,但这并不总是合适的......)
答案 1 :(得分:16)
This is now the default in python 3.6
Python 3.6.0a4+ (default:d43f819caea7, Sep 8 2016, 13:05:34)
>>> def func(**kw): print(kw.keys())
...
>>> func(a=1, b=2, c=3, d=4, e=5)
dict_keys(['a', 'b', 'c', 'd', 'e']) # expected order
如其他答案所述,之前无法做到这一点。
答案 2 :(得分:2)
当Python在签名中遇到**kwargs
构造时,它期望kwargs
成为"映射",这意味着两件事:(1)能够调用{ {1}}获取映射包含的密钥的可迭代次数,以及(2)可以为kwargs.keys()
返回的可迭代中的每个密钥调用kwargs.__getitem__(key)
,并且得到的值是所需的一个与该密钥相关联。
在内部,Python将"转换"无论映射到字典中,都是这样的:
keys()
如果您认为**kwargs -> {key:kwargs[key] for key in kwargs.keys()}
已经是kwargs
,这看起来有点傻 - 而且它会是,因为没有理由从一个dict
构建完全等效的dict
传入。
但是当kwargs
不一定是dict
时,重要的是将其内容放入合适的默认数据结构中,以便执行参数解包的代码始终知道它是什么&#39与...合作。
因此,可以混淆某种数据类型被解压缩的方式,但是由于为了一致的arg-unpacking-protocol而转换为dict
,它只是发生在参数解包的顺序上放置保证是不可能的(因为dict
没有跟踪添加元素的顺序)。如果Python的语言将**kwargs
放入OrderedDict
而不是dict
(意味着键'作为关键字args的顺序将是它们被遍历的顺序),然后通过传递OrderedDict
或其他数据结构keys()
尊重某种排序,你可以期望对参数进行某种排序。它只是实现的一个怪癖,dict
被选为标准,而不是其他类型的映射。
这是一个可以解开包装的课程的愚蠢例子。"但它总是将所有未打包的值视为42(尽管它们并非真的):
class MyOrderedDict(object):
def __init__(self, odict):
self._odict = odict
def __repr__(self):
return self._odict.__repr__()
def __getitem__(self, item):
return 42
def __setitem__(self, item, value):
self._odict[item] = value
def keys(self):
return self._odict.keys()
然后定义一个函数来打印解压缩的内容:
def foo(**kwargs):
for k, v in kwargs.iteritems():
print k, v
并制作一个值并尝试一下:
In [257]: import collections; od = collections.OrderedDict()
In [258]: od['a'] = 1; od['b'] = 2; od['c'] = 3;
In [259]: md = MyOrderedDict(od)
In [260]: print md
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
In [261]: md.keys()
Out[261]: ['a', 'b', 'c']
In [262]: foo(**md)
a 42
c 42
b 42
这种自定义的键值对传递(这里,dumbly总是返回42)是你能够修改**kwargs
在Python中的工作方式的能力。
稍微更灵活地修改*args
如何解包。有关详细信息,请参阅此问题:< Does argument unpacking use iteration or item-getting?>。