explicacion - working with generators in python
"Rendimiento de iterable" frente a "return iter(iterable)" (1)
Cuando se ajusta un iterador (interno), a menudo hay que redireccionar el método __iter__
al iterable subyacente. Considere el siguiente ejemplo:
class FancyNewClass(collections.Iterable):
def __init__(self):
self._internal_iterable = [1,2,3,4,5]
# ...
# variant A
def __iter__(self):
return iter(self._internal_iterable)
# variant B
def __iter__(self):
yield from self._internal_iterable
¿Hay alguna diferencia significativa entre la variante A y B? La variante A devuelve un objeto iterador que se ha consultado a través de iter()
desde el iterable interno. La variante B devuelve un objeto generador que devuelve valores del iterable interno. ¿Es uno u otro preferible por alguna razón? En collections.abc
se utiliza el yield from
versión. La variante de return iter()
es el patrón que he usado hasta ahora.
La única diferencia significativa es qué sucede cuando se genera una excepción desde dentro de lo iterable. Al usar return iter()
su FancyNewClass
no aparecerá en el FancyNewClass
excepciones, mientras que con el yield from
él aparecerá. Por lo general, es bueno tener la mayor cantidad de información posible sobre el rastreo, aunque puede haber situaciones en las que desee ocultar su envoltorio.
Otras diferencias:
return iter
tiene que cargar el nombreiter
desde globals: esto es potencialmente lento (aunque es improbable que afecte significativamente el rendimiento) y podría tener problemas (aunque cualquier persona que sobrescriba globals como esa merece lo que obtiene).Con el
yield from
usted puede insertar otras expresiones deyield
antes y después (aunque también puede usaritertools.chain
).Tal como se presentó, el
yield from
formulario descarta cualquier valor de retorno del generador (es decir,raise StopException(value)
. Puede corregir esto escribiendo, enreturn (yield from iterator)
.
Aquí hay una prueba que compara el desmontaje de los dos enfoques y también muestra las trazas de excepción: http://ideone.com/1YVcSe
Utilizando return iter()
:
3 0 LOAD_GLOBAL 0 (iter)
3 LOAD_FAST 0 (it)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 RETURN_VALUE
Traceback (most recent call last):
File "./prog.py", line 12, in test
File "./prog.py", line 10, in i
RuntimeError
Usando return (yield from)
:
5 0 LOAD_FAST 0 (it)
3 GET_ITER
4 LOAD_CONST 0 (None)
7 YIELD_FROM
8 RETURN_VALUE
Traceback (most recent call last):
File "./prog.py", line 12, in test
File "./prog.py", line 5, in bar
File "./prog.py", line 10, in i
RuntimeError