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()
获取字典项目的列表,然后将每个项目的元组的顺序从 (键,值) 反转为 (值,键),然后对列表进行排序;由于 Python 根据元组的第一个项目对列表进行排序,因此 (反转的) 项目列表因此按值排序。如果需要,可以反转列表,然后将元组重新反转回 (键,值)。(但是,根据我的经验,反转的元组顺序对于大多数目的来说都很好,例如打印出列表。)
例如,给定一个出现次数计数:
>>> 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()
可以简单地让我们控制 (键,值) 顺序。
(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