はい、それはビルトインが一般的にCで実装されているという事実と関係があります。実際、Cコードは、の場合のように、単純な関数ではなく新しい型を導入することがよくありenumerateます。それらをCで記述すると、それらをより細かく制御でき、多くの場合、パフォーマンスが向上します。実際の欠点はないため、当然の選択です。
以下に相当するものを書くためにそれを考慮に入れてください:
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
C、つまりジェネレータの新しいインスタンスでは、実際のバイトコードを含むコードオブジェクトを作成する必要があります。これは不可能ではありませんが、Python C-APIを実装__iter__して__next__呼び出すだけの新しい型を作成するよりも簡単ではありません。さらに、別の型を持つことのその他の利点もあります。
したがって、の場合、enumerateそれreversedは単にそれがより良いパフォーマンスを提供し、より保守しやすいからです。
その他の利点は次のとおりです。
- type(eg)にメソッドを追加できます
chain.from_iterable。これは関数でも実行できますが、最初に関数を定義してから手動で属性を設定する必要があり、あまりきれいに見えません。
isinstanceiterablesで私たちをすることができます。これにより、いくつかの最適化が可能になる可能性があります(たとえば、isinstance(iterable, itertools.repeat)それがわかっている場合は、どの値が生成されるかがわかっているので、コードを最適化できる可能性があります。
編集:私が何を意味するのかを明確にするためだけに:
C、つまりジェネレータの新しいインスタンスでは、実際のバイトコードを含むコードオブジェクトを作成する必要があります。
インスタンスObjects/genobject.cを作成する唯一の関数を見ると、そのシグネチャは次のとおりです。PyGen_TypePyGen_New
PyObject *
PyGen_New(PyFrameObject *f)
ここで、をObjects/frameobject.c見ると、を作成するには、次の署名を持つを呼び出す必要PyFrameObjectがあることがわかります。PyFrame_New
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
PyObject *locals)
ご覧のとおり、インスタンスが必要です。sは、Pythonインタープリターが内部でバイトコードを表す方法です(たとえば、aは関数のバイトコードを表すことができます)。したがって、Cからインスタンスを作成するには、手動でバイトコードを作成する必要があります。この署名があるため、 sを作成するのはそれほど簡単ではありません。:PyCodeObjectPyCodeObjectPyCodeObjectPyGen_TypePyCodeObjectPyCode_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は、他のCコードからではなく、Pythonソースによって取得されることを意図しています。もちろん、Cで作成することもできますが、単純な新しいタイプを作成するよりも必要な文字数が少なくなるかどうかはまったくわかりません。