PEP 265 – 按值排序字典
- 作者:
- Grant Griffin <g2 at iowegian.com>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2001年8月8日
- Python 版本:
- 2.2
- 发布历史:
摘要
本PEP建议对字典进行“按值排序”操作。其主要好处在于为常见的Python惯用法提供“开箱即用”的支持,而这种惯用法目前的实现形式对初学者来说难以理解,对所有人来说都繁琐。
BDFL 声明
本PEP被否决,因为它的需求已在很大程度上通过Py2.4的sorted()
内置函数得以满足
>>> sorted(d.iteritems(), key=itemgetter(1), reverse=True)
[('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]
或者仅针对键
sorted(d, key=d.__getitem__, reverse=True)
['b', 'd', 'c', 'a', 'e']
此外,Python 2.5的heapq.nlargest()
函数解决了只查找少数几个最高值项的常见用例
>>> nlargest(2, d.iteritems(), itemgetter(1))
[('b', 23), ('d', 17)]
动机
字典的一个常见用途是计数,在第一次出现时将d[key]
的值设置为1,然后在每次后续出现时递增该值。这可以通过几种不同的方式完成,但get()
方法是最简洁的
d[key] = d.get(key, 0) + 1
一旦所有出现都已计数,对结果字典的一个常见用途是以按出现次数排序的顺序打印出现次数,通常是最大值优先。
这就需要按值对字典的项进行排序。在Python中实现此操作的规范方法是:首先使用d.items()
获取字典项的列表,然后将每个项的元组顺序从(key, value)反转为(value, key),然后对列表进行排序;由于Python根据元组的第一个项对列表进行排序,因此(反转的)项列表就按值排序了。如果需要,列表可以反转,并且元组可以重新反转回(key, value)。(但是,根据我的经验,反转后的元组顺序对于大多数目的来说都很好,例如打印列表。)
例如,给定出现次数
>>> d = {'a':2, 'b':23, 'c':5, 'd':17, 'e':1}
我们可能会这样做
>>> items = [(v, k) for k, v in d.items()]
>>> items.sort()
>>> items.reverse() # so largest is first
>>> items = [(k, v) for v, k in items]
结果是
>>> items
[('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]
这显示了按值排序的列表,最大值优先。(在这种情况下,发现'b'
出现次数最多。)
这工作得很好,但在两个方面“难以使用”。首先,尽管这种惯用法为经验丰富的Python程序员所熟知,但对新手来说却一点也不明显——无论是从其算法(反转项元组的顺序)还是其实现(使用列表推导式——这是Python的高级特性)而言。其次,它需要反复输入大量“繁琐”的代码,导致乏味和错误。
因此,我们更希望Python提供一种按值对字典进行排序的方法,这种方法对新手来说既容易理解(或者更好的是,不需要去理解),也更容易让所有人使用。
基本原理
正如Tim Peters所指出的,这种事情会带来试图满足所有人的问题。因此,我们将限制其范围,以尝试达到“最佳点”。不寻常的情况(例如通过自定义比较函数排序)当然可以使用现有方法“手动”处理。
这里有一些简单的可能性
字典的items()
方法可以增加新的参数,这些参数具有默认值,可提供完全的向后兼容性
(1) items(sort_by_values=0, reversed=0)
或者可能只是
(2) items(sort_by_values=0)
因为反转列表足够容易。
或者,items()
可以简单地让我们控制(key, value)的顺序
(3) items(values_first=0)
同样,这完全向后兼容。它比其他方法做的工作少,但至少缓解了按值排序问题中最复杂/棘手的部分:反转项元组的顺序。使用起来非常简单
items = d.items(1)
items.sort()
items.reverse() # (if desired)
前三种方法的主要缺点是无参数items()
情况的额外开销,因为需要处理默认参数。(但是,如果假设items()
主要用于创建按值排序的列表,这在实践中并不是真正的缺点。)
或者,我们可能会添加一个新的字典方法,该方法以某种方式体现“排序”。这种方法提供了两个优点。首先,它避免了增加items()
方法的开销。其次,它可能对新手更易于访问:当他们寻找排序字典的方法时,他们有望找到这个方法,并且他们不必理解元组反转和列表排序的细微之处即可实现按值排序。
为了允许按键/值排序以及正向/反向排序这四种基本可能性,我们可以添加此方法
(4) sorted_items(by_value=0, reversed=0)
我相信最常见的情况实际上是by_value=1, reversed=1
,但此处给出的默认值可能会减少用户的意外:sorted_items()
将与items()
后跟sort()
相同。
最后(作为最后的手段),我们可以使用
(5) items_sorted_by_value(reversed=0)
实施
提议的字典方法必然会在C中实现。据推测,实现会相当简单,因为它只涉及向Python现有机制添加几个调用。
关注点
除了可能性1到3中已经解决的运行时开销之外,对该提案的担忧可能属于“特性膨胀”和/或“代码膨胀”类别。然而,我相信此处提出的几个建议将导致非常小的膨胀,从而在膨胀和“增值”之间取得良好的权衡。
Tim Peters指出,在C中实现此功能可能不会比今天在Python中实现它显著更快。然而,此处旨在实现的主要好处是“可访问性”和“易用性”,而不是“速度”。因此,只要它没有明显变慢(在普通items()
的情况下,速度无需考虑)。
参考资料
2001年8月,comp.lang.python上出现了一个名为“计数出现次数”的相关帖子。其中包括通过将其实现为可重用的Python函数和类来系统化按值排序问题的示例方法。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0265.rst