为什么在Python中反转和排序不同的类型?

39 python types python-internals

reversed的类型是"类型":

>>> type(reversed)
<class 'type'>
Run Code Online (Sandbox Code Playgroud)

sorted的类型是"内置函数或方法":

>>> type(sorted)
<class 'builtin_function_or_method'>
Run Code Online (Sandbox Code Playgroud)

但是,它们在性质上看起来是一样的.排除功能上的明显差异(逆转与排序序列),实现这种差异的原因是什么?

MSe*_*ert 51

区别在于它reversed是一个迭代器(它也是懒惰的评估),并且sorted是一个"热切"工作的函数.

所有内置迭代器(至少在python-3.X)一样map,zip,filter,reversed,...被实现为.虽然急于营业内建命令的功能,例如min,max,any,allsorted.

>>> a = [1,2,3,4]
>>> r = reversed(a)
<list_reverseiterator at 0x2187afa0240>
Run Code Online (Sandbox Code Playgroud)

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

>>> list(r)
[4, 3, 2, 1]
Run Code Online (Sandbox Code Playgroud)

在另一方面不需要这种"消费"的部分功能,sorted:

>>> s = sorted(a)
[1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

在评论中,有人问为什么这些是作为类而不是函数实现的.这不是很容易回答,但我会尽我所能:

使用延迟评估操作有一个巨大的好处:链接时它们的内存效率非常高.除非明确"请求",否则他们不需要创建中间列表.这就是为什么map,zipfilter从热切操作函数(python-2.x)更改为惰性操作类(python-3.x)的原因.

通常,Python有两种方法来创建迭代器:

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

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

使用类而不是生成器还有另外一个原因:您可以为类实现特殊方法,但不能在生成器函数上实现它们.这听起来可能并不令人印象深刻,但它有一定的优 例如,可以使用和方法对大多数迭代器进行pickle(至少在Python-3.x上).这意味着您可以将它们存储在磁盘上,并允许复制它们.从Python-3.4开始,一些迭代器也会实现,这使得使用(和类似)迭代器的速度要快得多.__reduce____setstate____length_hint__list


注意,reversed可以很容易地实现为工厂函数(如iter),但不同的是iter,它可以返回两个唯一的类,reversed只能返回一个唯一的类.

为了说明可能的(和唯一的)类,您必须考虑一个__iter__没有__reversed__方法但没有方法但可迭代和反向迭代的类(通过实现__getitem____len__):

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]
Run Code Online (Sandbox Code Playgroud)

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

>>> 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>
Run Code Online (Sandbox Code Playgroud)

这种推理不适用于reversed:

>>> reversed(A([1,2,3]))
<reversed at 0x2187afaec50>
Run Code Online (Sandbox Code Playgroud)

  • @cᴏʟᴅsᴘᴇᴇᴅ:"函数必须构造某个类的某个实例." - 是的,但是`sorted`也是如此,返回一个新对象的函数也是如此. (2认同)

cs9*_*s95 5

reversed和 和有什么区别sorted

有趣的是,reversedis 不是一个函数,而 whilesorted是。

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

class reversed(object)
 |  reversed(sequence) -> reverse iterator over values of the sequence
 |  
 |  Return a reverse iterator
Run Code Online (Sandbox Code Playgroud)

它确实是一个用于返回反向迭代器的类。

好吧,所以这reversed不是一个函数。但为什么不呢?

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

  • 如果我正确理解OP的问题,他们会问_为什么_“reversed”不是一个函数。(或者为什么“sorted”不是一种类型。) (3认同)