Tak, ma to związek z tym, że wbudowane generalnie są realizowane w C. Bardzo często kod C wprowadza nowe typy zamiast zwykłych funkcji, jak w przypadku enumerate
. Pisanie ich w C zapewnia lepszą kontrolę nad nimi, a często pewne ulepszenia wydajności, , a ponieważ nie ma prawdziwej wady, jest to naturalny wybór.
Weź pod uwagę, że do pisania równowartość:
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
w C, to znaczy nowej instancji generatora, należy utworzyć obiekt kodu, który zawiera aktualne kodu bajtowego. Nie jest to niemożliwe, ale nie jest to łatwiejsze niż napisanie nowego typu, który po prostu implementuje __iter__
i __next__
wywoływanie C-API Pythona plus inne zalety posiadania innego typu.
Tak, w przypadku enumerate
i reversed
jest to po prostu dlatego, że zapewnia lepszą wydajność i jest łatwiejsza w utrzymaniu.
Inne zalety to:
- Możesz dodać metody do typu (np
chain.from_iterable
.). Można to zrobić nawet za pomocą funkcji, ale najpierw musisz je zdefiniować, a następnie ręcznie ustawić atrybuty, które nie wyglądają tak czysto.
- Możesz nam
isinstance
na iterables. Może to pozwolić na pewne optymalizacje (na przykład, jeśli wiesz, że isinstance(iterable, itertools.repeat)
, możesz być w stanie zoptymalizować kod, ponieważ wiesz, które wartości zostaną uzyskane.
Edit: Właśnie w celu wyjaśnienia, co mam na myśli:
w C, czyli nowej instancji generatora, należy utworzyć kod obiekt, który zawiera aktualne kodu bajtowego.
Patrząc na Objects/genobject.c
jedyną funkcją, aby utworzyć instancję PyGen_Type
jest PyGen_New
którego podpis jest:
PyObject *
PyGen_New(PyFrameObject *f)
Teraz, patrząc na Objects/frameobject.c
widzimy, że aby stworzyć PyFrameObject
ty koniecznością połączenia PyFrame_New
, z tym podpisem:
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
PyObject *locals)
Jak widać, wymaga instancji a PyCodeObject
. PyCodeObject
s W jaki sposób interpreter python reprezentuje kod bajtowy wewnętrznie (np. PyCodeObject
może reprezentować kod bajtowy funkcji), więc: tak, aby utworzyć instancję PyGen_Type
z C, należy ręcznie utworzyć kod bajtowy, a tworzenie go nie jest takie proste PyCodeObject
s od PyCode_New
ma ten podpis:
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)
uwaga jak to zawiera argumenty takie jak firstlineno
, filename
które są oczywiście miało być uzyskane przez źródła Pythona, a nie z innym kodem C. Oczywiście możesz go utworzyć w C, ale nie jestem wcale pewien, czy wymagałoby to mniej znaków niż pisanie prostego nowego typu.
Czy to jest problem? Funkcje i klasy są po prostu obiektami wywoływalnymi ... – JBernardo
@JBernardo To nie jest problem w prawie wszystkich okolicznościach (a kiedy tak jest, prawdopodobnie powinieneś po prostu naprawić hack, który się psuje). Ale nadal jest interesująco. – delnan
Nie, oczywiście, że nie. To tylko kwestia akademicka. Chcę poznać uzasadnienie ich wdrożenia, kiedy wdrażanie generatorów jest tak proste. Być może pozwoli mi to trochę wniknąć w kwestię: czy powinienem to robić w ten sposób dla własnych generatorów? –