working with generators explicacion creating python python-3.x iterable yield-from

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 nombre iter 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 de yield antes y después (aunque también puede usar itertools.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, en return (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