python - 为什么某些 python 内置的"functions" 实际上是?

  显示原文与译文双语对照的内容
0 0

许多迭代器" 中的函数" __builtin__模块实际上是实现为类型,甚至虽然该文档谈论它们作为被" 函数" 。 把例如 enumerate. 文档表明,它等效于:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

这是一句话我早就可以实现了,当然。 但是,我运行以下测试与以前的定义,来到这个:

>>> x = enumerate(range(10))
>>> x
<generator object enumerate at 0x01ED9F08>

它上了一个 然而,在使用 __builtin__版本我会很不舒服:

>>> x = enumerate(range(10))
>>> x
<enumerate object at 0x01EE9EE0>

从此我推断,该系数定义如下:

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

而不是标准形成文档中的说明。 我终于能理解其中的工作原理,以及它等效于标准形式中,我想知道有什么理由这样做。 这样是否更有效? 它是和麦田怪圈有关,这些函数要实现在C ( I 不知道他们,但我怀疑所以)?

我使用Python 2 .7 .2,万一的区别很重要。

感谢你提前。

时间:原作者:6个回答

0 0

是的,它必须处理这一事实ins通常是生成C 中实现。 很C代码经常会引入新的类型,而不是纯函数,例如在 enumerate. 通常写为C 中更好地控制它们,并提供一些性能改进,因为没有真正的缺点是它是一个自然的选择。

考虑到编写等效的操作:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

在C,我如果不包含。 生成器,你应该创建一个代码对象的一个新实例,它包含实际的字节码。 这是可行的,但是不是可以这么容易编写新类型,它简单地实现 __iter____next__调用Python C API,以及其他具有不同类型的优点。

所以,如果是 enumeratereversed原因很简单,因为它能提供更高的性能和更容易维护。

其他优点包括:

  • 你可以添加方法到type( e .g 。 chain.from_iterable) 。 还可能是做了3 个函数,但是你必须先定义它们,然后手工设置这些属性,这看起来也没那么干净。
  • 我们可以 isinstance在iterables 。 这可能影响一些optimizations( e .g如果知道 isinstance(iterable, itertools.repeat),则你也许能够优化代码,因为你知道哪些值就认输了。

编辑: 问清楚我这样说是指:

在C,我如果不包含。 生成器,则应创建一个代码对象的一个新实例,它包含实际的字节码。

在看 Objects/genobject.c只有函数创建 PyGen_Type实例时, PyGen_New其签名如下:

PyObject *
PyGen_New(PyFrameObject *f)

现在,在看 Objects/frameobject.c可以看出,创建一个 PyFrameObject你必须 调用PyFrame_New也就是这个美丽的签名:

PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
            PyObject *locals)

你可以看到它需要PyCodeObject实例。 PyCodeObjects 是当执行一个python解释器时字节码internally( e .g 。 a PyCodeObject可以表示的字节码函数),即: yes,以创建一个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)

请注意其中包含参数如 firstlineno, filename这当然意味着要想通过python源文件,而不是从其他C代码获得。 C 中显然可以创建公用文件夹,可我一点也不相信,它将需要的字符比编写一个简单的新类型。

原作者:
0 0

是的,他们是用C 实现。 不过是用C 的迭代器( API PEP 234 ),在其中定义了迭代器通过创建新类型有 tp_iternext槽。

是由生成器函数的语法( 创建文件的函数 yield) 是'神秘的'函数返回一个特殊的生成器对象。 这是晚接孩子的例子 types.GeneratorType你无法手动创建。 如果不同的库使用C 的API定义了自己的迭代器类型,它将不会被实例, GeneratorType,但是仍然实施C API迭代器协议。

因此, enumerate类型是一种特殊的类型不同于 GeneratorType,你可以使用它的方式与任何其他类型, isinstance和( 但是你不应该这样) 。


与Bakuriu是回答, enumerate不是生成器,因此没有bytecode/frames 。

$ grep -i 'frame|gen' Objects/enumobject.c
    PyObject_GenericGetAttr,        /* tp_getattro */
    PyType_GenericAlloc,            /* tp_alloc */
    PyObject_GenericGetAttr,        /* tp_getattro */
    PyType_GenericAlloc,            /* tp_alloc */

反之,你新建一个enumobject的方式是以函数的形式 enum_new不使用框架,他的签名

static PyObject *
enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

此函数是放在了 tp_new插槽。 PyEnum_Typestruct ( 类型为 PyTypeObject) 。 在这里,我们也看到, tp_iternext插槽已被占用。 enum_next函数,该函数包含简单C代码的枚举,然后返回的是一个迭代器,用来获取下一项PyObject ( 元组) 。

向前移动, PyEnum_Type然后被放入到内部模块( Python/bltinmodule.c与该名称) enumerate,使其可以被公共访问。

无需使用字节码。 单纯的C 。 效率比所有的python或 GeneratorType实现。

原作者:
0 0

enumerate调用需要返回一个迭代器。 iterator是一个对象与特定API 。 最简单的方法实现类的特定API通常,嗯,实现其作为类。

之所以上面写着" 类型" 而不是" 类" Python 2 具体,如内置类调用了" 类型" Python 2 中,作为其他Python库同时使用类型和类之前Python 2 .2 。 在Python 2 .3类和类型是统一的。 然后在Python 3 因此公司说类:

>>> enumerate
<class 'enumerate'>

这使得强调我们你的问题 " 为什么某些内建类型而不是函数" 时几乎不和他们一起被C 中实现。 他们types/classes因为那是最好的方法来实现功能。 就这么简单。

现在如果我们而你的问题解释为 " 为什么enumeratetype/class而不是生成器" ( ),那么这是一个非常不同的问题答案也自然不同。 从Python答案有发电机为快捷方式Python用于创建迭代器的功能。 他们不打算让从C 。 它们也是用来制作生成器离开函数少于离开类方法,因为如果要创建iterator对象移出类方法也需要传入对象上下文,就是用函数则不需要windows metafile 。 所以它主要是能使你有更少的" 脚手架" 代码。

原作者:
...