为什么在 Python 中对不同类型进行倒序排序?

Why are reversed and sorted of different types in Python?

reversed的类型是"type":

>>> type(reversed)
<class 'type'>

sorted的类型是"builtin function or method":

>>> type(sorted)
<class 'builtin_function_or_method'>

然而,它们在本质上看起来是一样的。排除功能上的明显差异(反转序列与排序序列),实现上存在差异的原因是什么?

What's the difference between reversed and sorted?

有趣的是,reversed 不是函数,而 sorted 是。

打开一个 REPL 会话并输入 help(reversed):

class reversed(object)
 |  reversed(sequence) -> reverse iterator over values of the sequence
 |  
 |  Return a reverse iterator

确实是一个class,用来return一个反向迭代器。

Okay, so reversed isn't a function. But why not?

这个问题有点难回答。一种解释是迭代器具有惰性求值。这需要某种容器来存储有关迭代器在任何给定时间的当前状态的信息。这最好通过一个对象来完成,因此,class.

区别在于 reversed 是一个迭代器(它也是惰性求值的)而 sorted 是一个可以工作的函数 "eagerly".

所有内置迭代器(至少在 python-3.x),如 mapzipfilterreversed , ... 实现为 classes。虽然急切操作的内置函数是 函数 ,例如minmaxanyallsorted.

>>> a = [1,2,3,4]
>>> r = reversed(a)
<list_reverseiterator at 0x2187afa0240>

您实际上需要 "consume" 迭代器来获取值(例如 list):

>>> list(r)
[4, 3, 2, 1]

另一方面,函数不需要这个"consuming"部分,比如sorted:

>>> s = sorted(a)
[1, 2, 3, 4]

在评论中有人问 为什么这些被实现为 classes 而不是函数 。这不是很容易回答,但我会尽力而为:

使用惰性求值操作有一个巨大的好处:它们在链接时非常节省内存。他们不需要创建中间列表,除非它们是明确的 "requested"。这就是为什么 mapzipfilter 从急切操作函数 (python-2.x) 更改为惰性操作 classes (python-3.x).

一般在Python中有两种创建迭代器的方法:

  • class在他们的 __iter__ 方法中 return self
  • 生成器函数 - 包含 yield
  • 的函数

但是(至少 CPython)在 C 中实现了所有内置函数(和几个标准库模块)。在 C 中创建迭代器 classes 非常容易,但我没有我没有找到任何明智的方法来创建基于 Python-C-API 的生成器函数。因此,将这些迭代器实现为 classes(在 CPython 中)的原因可能只是方便或缺乏(快速或可实现的)替代方案。

使用 classes 而不是生成器还有一个额外的原因:您可以为 classes 实现特殊方法,但不能在生成器函数上实现它们。这听起来可能并不令人印象深刻,但它具有明确的优势。例如,大多数迭代器可以是 pickled (at least on Python-3.x) using the __reduce__ and __setstate__ methods. That means you can store them on the disk, and allows copying them. Since Python-3.4 some iterators also implement __length_hint__,这使得使用 list(和类似)更快地使用这些迭代器。


请注意,reversed 可以很容易地实现为工厂函数(如 iter),但不像 iter,后者可以 return 两个唯一的 classes,reversed只能return 一个唯一 class.

为了说明可能的(且唯一的)classes,您必须考虑没有 __iter__ and no __reversed__ method but are iterable and reverse-iterable (by implementing __getitem__ and __len__ 的 class:

class A(object):
    def __init__(self, vals):
        self.vals = vals

    def __len__(self):
        return len(self.vals)

    def __getitem__(self, idx):
        return self.vals[idx]

虽然在 iter 的情况下添加抽象层(工厂函数)是有意义的 - 因为 returned class 取决于输入参数的数量:

>>> iter(A([1,2,3]))
<iterator at 0x2187afaed68>
>>> iter(min, 0)   # actually this is a useless example, just here to see what it returns
<callable_iterator at 0x1333879bdd8>

该推理不适用于 reversed

>>> reversed(A([1,2,3]))
<reversed at 0x2187afaec50>