ordereddict from dict create python generator

ordereddict - python create dictionary from list



(list | set | dict) la comprensión que contiene una expresión de rendimiento no devuelve a(list | set | dict) (2)

Python 3.3

He construido esta pieza ligeramente críptica de python 3.3:

>>> [(yield from (i, i + 1, i)) for i in range(5)] <generator object <listcomp> at 0x0000008666D96900> >>> list(_) [0, 1, 0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4]

Si uso un generador de comprensión dentro de un constructor de listas, obtengo un resultado diferente:

>>> list((yield from (i, i + 1, i)) for i in range(5)) [0, 1, 0, None, 1, 2, 1, None, 2, 3, 2, None, 3, 4, 3, None, 4, 5, 4, None]

¿Por qué la lista de comprensión no devuelve una lista?

Python 2.7

Puedo obtener un efecto extraño similar en python 2 (usando una comprensión establecida, porque las comprensiones de lista tienen un alcance impar):

>>> {(yield i) for i in range(5)} <generator object <setcomp> at 0x0000000004A06120> >>> list(_) [0, 1, 2, 3, 4, {None}]

Y cuando se utiliza un generador de comprensión:

>>> list((yield i) for i in range(5)) [0, None, 1, None, 2, None, 3, None, 4, None]

¿De dónde vino esa {None} ?


Las comprensiones de lista (conjunto, dict) se traducen a una estructura de código diferente de las expresiones generadoras. Veamos un conjunto de comprensión:

def f(): return {i for i in range(10)} dis.dis(f.__code__.co_consts[1]) 2 0 BUILD_SET 0 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 12 (to 21) 9 STORE_FAST 1 (i) 12 LOAD_FAST 1 (i) 15 SET_ADD 2 18 JUMP_ABSOLUTE 6 >> 21 RETURN_VALUE

Compare con la expresión del generador equivalente:

def g(): return (i for i in range(10)) dis.dis(g.__code__.co_consts[1]) 2 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 11 (to 17) 6 STORE_FAST 1 (i) 9 LOAD_FAST 1 (i) 12 YIELD_VALUE 13 POP_TOP 14 JUMP_ABSOLUTE 3 >> 17 LOAD_CONST 0 (None) 20 RETURN_VALUE

Notará que donde la expresión del generador tiene un yield , la comprensión del conjunto almacena un valor directamente en el conjunto que está creando.

Esto significa que si agrega una expresión de yield en el cuerpo de una expresión de generador, se trata indistintamente del yield que el lenguaje construye para el cuerpo del generador; como resultado, obtienes dos (o más) valores por iteración.

Sin embargo, si agrega un yield a una comprensión de lista (set, dict), la comprensión se transforma de una función que construye una lista (set, dict) en un generador que ejecuta las declaraciones de yield y luego devuelve la lista construida (set, dict) . El {None} en el resultado de la comprensión del conjunto es el conjunto construido a partir de cada una de las None las que se evalúan las expresiones de yield .

Finalmente, ¿por qué Python 3.3 no produce un {None} ? (Tenga en cuenta que las versiones anteriores de Python 3 sí lo hacen). Es debido a PEP 380 (también conocido como yield from soporte). Antes de Python 3.3, un return en un generador es un SyntaxError: ''return'' with argument inside generator ; Por lo tanto, nuestras comprensiones de yield están explotando un comportamiento indefinido, pero el resultado real del opcode RETURN_VALUE es generar simplemente otro valor (final) del generador. En Python 3.3, el return value se admite explícitamente; un RETURN_VALUE operación RETURN_VALUE hace que se RETURN_VALUE una StopIteration , que tiene el efecto de detener el generador sin producir un valor final.


Usando esto como una referencia:

Explicación de Python 3

Esta:

values = [(yield from (i, i + 1, i)) for i in range(5)]

Se traduce a lo siguiente en Python 3.x:

def _tmpfunc(): _tmp = [] for x in range(5): _tmp.append(yield from (i, i + 1, i)) return _tmp values = _tmpfunc()

Lo que resulta en values contienen un generador.

Ese generador luego cederá de cada (i, i + 1, i) , hasta que finalmente alcance la declaración de retorno. En Python 3, esto generará throw StopIteration(_tmp) ; sin embargo, el constructor de list ignora esta excepción.

Por otro lado, esto:

list((yield from (i, i + 1, i)) for i in range(5))

Se traduce a lo siguiente en Python 3.x:

def _tmpfunc(): for x in range(5): yield (yield from (i, i + 1, i)) values = list(_tmpfunc())

Esta vez, cada vez que se completa el yield from , se evalúa como None , que luego se yield entre los otros valores.