Python 字典排序,Python字典是无序的,因此迭代时无法确保能以相同的顺序得到字典元素(从Python 3.6开始,字典会保有顺序)。
但有时需要根据某项属性,如字典的键、值或其他派生属性对字典中的项排序。假设有一个带有以下键值对的字典xs
:
>>> xs = {'a': 4, 'c': 2, 'b': 3, 'd': 1}
为了获得以字典中键值对组成的有序列表,可以先使用字典的items()
方法获得列表,接着对其排序:
>>> sorted(xs.items())
[('a', 4), ('b', 3), ('c', 2), ('d', 1)]
这个键值对的元组根据Python中的标准词典顺序排列。
比较两个元组时,Python首先比较存储在索引0位置的项。如果不相同,则返回比较结果;如果相同,则继续比较索引1处的两个项,以此类推。
因为这个元组来自于字典,所以每个元组中索引0的字典键都是唯一的,排序不会破坏字典中已有的关系。
我们有时候需要按字典的键来排序,但有些时候则希望按值对字典排序。
幸运的是,有一种方法可以控制字典项的排序方式。向sorted()
函数传递一个key函数能够改变字典项的比较方式。
key函数是一个普通的Python函数,在比较之前会在每个元素上调用。key函数将一个字典项作为其输入,然后为排序返回比较所需的key。
这里在不同语境中使用了“key”这个词,key函数和字典的键(key)无关,前者只是将每个输入项映射成一个用于比较的key。
来看一个例子,通过真实的代码来帮助理解key函数。
如果想根据字典项的值来排序字典,可以使用下面的key函数,这个函数会返回键值对元组中的第二个元素:
>>> sorted(xs.items(), key=lambda x: x[1])
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
现在得到了基于原字典中的值排序得到的键值对列表。key函数的概念很强大,能应用于许多Python情形,因此值得花一些时间来掌握其工作方式。
事实上,由于这个概念及其常见,因此Python的标准库包含了operator
模块。operator
模块将一些常用的key函数实现为即插即用的组件,如operator.itemgetter
和operator.attrgetter
。
下面这个示例用operator.itemgetter
替换了第一个示例中基于lambda的索引查找:
>>> import operator
>>> sorted(xs.items(), key=operator.itemgetter(1))
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
有时使用operator
模块能更清楚地传达代码的意图,但有时使用简单的lambda表达式编写的代码就已经有足够的可读性且含义更加明确。在这个特定的例子中,我更喜欢lambda表达式。
使用lambda作为自定义key函数的另一个好处是可以更细致地控制排列顺序。例如,还可以根据存储的每个值的绝对值对字典排序:
>>> sorted(xs.items(), key=lambda x: abs(x[1]))
如果需要逆序排列以便把最大值放在前面,则可以在调用sorted()
时使用reverse=True
关键字参数:
>>> sorted(xs.items(),
key=lambda x: x[1],
reverse=True)
[('a', 4), ('b', 3), ('c', 2), ('d', 1)]
就像刚刚说的那样,值得花费一些时间来掌握Python中key函数的工作方式。key函数提供了很大的灵活性,通常可以省去编写用于在不同数据结构之间转换的代码。
关键要点
-
在创建字典和其他集合的有序“视图”时,可以通过key函数决定排序方式。
-
key函数是Python中的一个重要概念,标准库中的
operator
模块添加了许多经常使用的key函数。 -
函数是Python中的一等公民,是在Python中无处不在的强大特性。