为什么一些python内置"函数"实际上是类型?

Ale*_*iad 12 python

__builtin__模块中的许多迭代器"函数" 实际上都是作为类型实现的,即使文档将它们称为"函数".举个例子enumerate.文档说它相当于:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1
Run Code Online (Sandbox Code Playgroud)

当然,这与我实施的完全一样.但是,我使用之前的定义运行了以下测试,并得到了:

>>> x = enumerate(range(10))
>>> x
<generator object enumerate at 0x01ED9F08>
Run Code Online (Sandbox Code Playgroud)

这是我的期望.但是,在使用该__builtin__版本时,我得到了这个:

>>> x = enumerate(range(10))
>>> x
<enumerate object at 0x01EE9EE0>
Run Code Online (Sandbox Code Playgroud)

由此我推断它被定义为

class enumerate:
    def __init__(self, sequence, start=0):
        # ....

    def __iter__(self):
        # ...
Run Code Online (Sandbox Code Playgroud)

而不是文档显示的标准形式.现在我可以理解它是如何工作的,以及它是如何与标准形式相同的,我想知道的是这样做的原因是什么.这种方式更有效吗?它是否与在C中实现的这些函数有关(我不知道它们是否存在,但我怀疑如此)?

我正在使用Python 2.7.2,以防万一差异很重要.

提前致谢.

Bak*_*riu 9

是的,它与内置函数通常用C实现的事实有关.通常C代码将引入新类型而不是普通函数,如同的情况一样enumerate.用C语言编写它们可以更好地控制它们,并且通常会有一些性能改进,而且由于没有真正的缺点,它是一个自然的选择.

考虑到要写相当于:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1
Run Code Online (Sandbox Code Playgroud)

在C中,即生成器的新实例,您应该创建一个包含实际字节码的代码对象.这并非不可能,但并不比编写一个简单实现__iter____next__调用Python C-API 的新类型以及具有不同类型的其他优点更容易.

所以,在这种情况下enumerate,reversed它只是因为它提供了更好的性能,而且它更易于维护.

其他优点包括:

  • 您可以向该类型添加方法(例如chain.from_iterable).即使使用函数也可以这样做,但是您必须首先定义它们,然后手动设置属性,这看起来不那么干净.
  • 你可以isinstance在iterables上.这可以允许一些优化(例如,如果您知道isinstance(iterable, itertools.repeat),那么您可以优化代码,因为您知道将产生哪些值.

编辑:只是为了澄清我的意思:

在C中,即生成器的新实例,您应该创建一个包含实际字节码的代码对象.

查看Objects/genobject.c创建PyGen_Type实例的唯一函数是PyGen_New其签名是:

PyObject *
PyGen_New(PyFrameObject *f)
Run Code Online (Sandbox Code Playgroud)

现在,看着Objects/frameobject.c我们可以看到创建一个PyFrameObject必须调用PyFrame_New,它有这个签名:

PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
            PyObject *locals)
Run Code Online (Sandbox Code Playgroud)

如您所见,它需要一个PyCodeObject实例.PyCodeObjects是python解释器如何在内部表示字节码(例如,a PyCodeObject可以表示函数的字节码),所以:是的,PyGen_Type要从C 创建实例,你必须手动创建字节码,并且创建PyCodeObjects 并不是那么容易因为PyCode_New这个签名:

PyCodeObject *
PyCode_New(int argcount, int kwonlyargcount,
           int nlocals, int stacksize, int flags,
           PyObject *code, PyObject *consts, PyObject *names,
           PyObject *varnames, PyObject *freevars, PyObject *cellvars,
           PyObject *filename, PyObject *name, int firstlineno,
           PyObject *lnotab)
Run Code Online (Sandbox Code Playgroud)

注意它是如何包含的参数firstlineno,filename显然是由python源而不是其他C代码获得的.显然你可以在C中创建它,但我不能确定它比写一个简单的新类型需要更少的字符.

  • 为什么用C编写的函数必须是新类型? (4认同)