valor tuplas tupla posiciones por operaciones listas ejemplo diccionarios diccionario convertir con comprensiĆ³n buscar python python-3.x dictionary

tuplas - posiciones diccionario python



Orden de operaciones en un diccionario de comprensiĆ³n. (4)

Como parece, la ventana emergente precede a la asignación de la lista x como el valor y es por eso que ''capitán'' no aparece en los valores (ya aparece)

No, el orden en que sucede es irrelevante. Está mutando la lista, por lo que verá la lista modificada después de la ventana emergente donde la use. Tenga en cuenta que, en general, es probable que no quiera hacer esto, ya que destruirá la lista original. Incluso si eso no importa esta vez, es una trampa para los incautos en el futuro.

En ambos casos, el lado del valor se calcula primero y luego la clave correspondiente. Es solo que en tu primer caso no importa mientras que en el segundo.

Puedes ver esto con bastante facilidad:

>>> def foo(a): print("foo", a) ... >>> def bar(a): print("bar", a) ... >>> { foo(a):bar(a) for a in (1, 2, 3) } (''bar'', 1) (''foo'', 1) (''bar'', 2) (''foo'', 2) (''bar'', 3) (''foo'', 3) {None: None} >>>

Tenga en cuenta que no debe escribir código que dependa de los valores que se evalúan primero: el comportamiento puede cambiar en versiones futuras (en algunos lugares se dijo que había cambiado en Python 3.5 y versiones posteriores, aunque en realidad parece que no es así).

Una forma más sencilla de hacer esto, que evita mutar la estructura de datos original:

my_dict = {x[0]: x[1:] for x in my_list}

O tu segundo ejemplo:

my_headers = [''column1'', ''column2'', ''column3''] my_dict = {x[0]: {k: v for k, v in zip(my_headers, x[1:])} for x in my_list}

Para responder a los comentarios: el zip usa la x original porque se evalúa antes del pop , pero usa el contenido de la lista para construir una nueva lista, por lo que cualquier cambio posterior a la lista no se verá en el resultado. La primera comprensión también utiliza la x original como el valor, pero luego muta la lista para que el valor aún vea la lista original y, por lo tanto, la mutación.

Me encontré con la siguiente construcción interesante:

asumiendo que tiene una lista de listas de la siguiente manera:

my_list = [[''captain1'', ''foo1'', ''bar1'', ''foobar1''], [''captain2'', ''foo2'', ''bar2'', ''foobar2''], ...]

y desea crear un dictado de ellos con los elementos 0 -index como las claves. Una forma práctica de hacerlo sería esta:

my_dict = {x.pop(0): x for x in my_list} # {''captain1'': [''foo1'', ''bar1'', ''foobar1''], ...}

Como parece, la ventana pop precede a la asignación de la lista x como el valor y es por eso que ''captain'' no aparece en los valores (ya aparece)

Ahora vamos a dar un paso más y tratar de obtener una estructura como:

# {''captain1'': {''column1'': ''foo1'', ''column2'': ''bar1'', ''column3'': ''foobar1''}, ...}

Para esta tarea escribí lo siguiente:

my_headers = [''column1'', ''column2'', ''column3''] my_dict = {x.pop(0): {k: v for k, v in zip(my_headers, x)} for x in my_list}

pero esto vuelve:

# {''captain1'': {''col3'': ''bar1'', ''col1'': ''captain1'', ''col2'': ''foo1''}, ''captain2'': {''col3'': ''bar2'', ''col1'': ''captain2'', ''col2'': ''foo2''}}

por lo que el pop en este caso ocurre después de que se construye el diccionario interno (o al menos después del zip ).

¿Como puede ser? ¿Como funciona esto?

La pregunta no es sobre cómo hacerlo, sino por qué se ve este comportamiento.

Estoy usando la versión 3.5.1 de Python .


Como dije en un comentario, eso se debe a que, en un diccionario, Python evalúa el valor primero. Y como un enfoque más pitón, puede usar variables de desempaquetado para esta tarea, en lugar de hacer estallar de la lista en cada iteración:

In [32]: my_list = [[''captain1'', ''foo1'', ''bar1'', ''foobar1''], [''captain2'', ''foo2'', ''bar2'', ''foobar2'']] In [33]: {frist: {"column{}".format(i): k for i, k in enumerate(last, 1)} for frist, *last in my_list} Out[33]: {''captain2'': {''column3'': ''foobar2'', ''column1'': ''foo2'', ''column2'': ''bar2''}, ''captain1'': {''column3'': ''foobar1'', ''column1'': ''foo1'', ''column2'': ''bar1''}}

Con respecto al extraño comportamiento de python al evaluar las claves y los valores en una comprensión de diccionario, después de algunos experimentos, me di cuenta de que este comportamiento es de alguna manera razonable en lugar de ser un error.

Voy a romper mi impresión en las siguientes partes:

  1. En una expresión de asignación, python evalúa primero el lado derecho. del doc:

    Python evalúa expresiones de izquierda a derecha. Observe que al evaluar una asignación, el lado derecho se evalúa antes que el lado izquierdo.

  2. La comprensión del diccionario es una expresión y se evaluará de izquierda a derecha, pero dado que hay una tarea bajo el capó, después de traducirla por python. El valor que tiene el lado derecho se evaluará primero.

    por ejemplo la siguiente comprensión:

    {b.pop(0): b.pop(0) for _ in range(1)} es equivalente con el siguiente fragmento de código:

def dict_comprehension(): the_dict = {} for _ in range(1): the_dict[b.pop(0)] = b.pop(0) return the_dict

Aquí hay unos ejemplos:

In [12]: b = [4, 0] # simple rule : Python evaluates expressions from left to right. In [13]: [[b.pop(0), b.pop(0)] for _ in range(1)] Out[13]: [[4, 0]] In [14]: b = [4, 0] # while evaluating an assignment (aforementioned rule 1), the right-hand side is evaluated before the left-hand side. In [15]: {b.pop(0): b.pop(0) for _ in range(1)} Out[15]: {0: 4} In [16]: b = [4, 0] # This is not a dictionary comprehension and will be evaluated left to right. In [17]: {b.pop(0): {b.pop(0) for _ in range(1)}} Out[17]: {4: {0}} In [18]: b = [4, 0] # This is not a dictionary comprehension and will be evaluated left to right. In [19]: {b.pop(0): b.pop(0) == 0} Out[19]: {4: True} In [20]: b = [4, 0] # dictionary comprehension. In [21]: {b.pop(0): {b.pop(0) for _ in range(1)} for _ in range(1)} Out[21]: {0: {4}}

Con respecto a la disparidad entre el hecho (o es mejor decir abstracción) de que las comprensiones del diccionario son expresiones y deben evaluarse de izquierda a derecha (según la documentación de python) con los comportamientos observados, creo que en realidad es un problema e inmadurez de la documentación de python. No es un error en el código de Python. Debido a que no es razonable cambiar la funcionalidad debido a que tiene una documentación consistente sin excepción.


En realidad, su observación no requiere un pedido especial de la operación. La razón es que x.pop(0) modifica el objeto x . Entonces, si evalúa el valor ( x ) antes o después de la clave ( x.pop(0) ) no importa en este caso.

De todos modos, no creo que la especificación del lenguaje python prescriba un cierto orden de operaciones, lo que significa que no debes confiar en que el orden sea en particular.

En realidad, la implementación estándar sucede para evaluar el valor antes de evaluar la clave, pero no hay en ninguna parte del estándar donde se establezca esto. La única garantía es que los pares clave-valor se evalúan en orden de iteración y se insertan en ese orden.


tl; dr : Aunque Python primero evalúa los valores (el lado derecho de la expresión), esto parece ser un error en (C) Python de acuerdo con el manual de referencia y la gramática y el PEP en las comprensiones de dict .

Aunque esto se solucionó previamente para las visualizaciones de diccionarios donde los valores se evaluaron nuevamente antes de las claves, el parche no se modificó para incluir dictados. Este requisito también fue mencionado por uno de los core-devs en un hilo de lista de correo que trata este mismo tema .

De acuerdo con el manual de referencia, Python evalúa las expresiones de izquierda a derecha y las asignaciones de derecha a izquierda ; una comprensión de dictado es realmente una expresión que contiene expresiones, no una asignación * :

{expr1: expr2 for ...}

donde, de acuerdo con la regla correspondiente de la grammar uno esperaría que expr1: expr2 se evalúe de manera similar a lo que hace en las pantallas. Entonces, ambas expresiones deben seguir el orden definido, expr1 debe evaluarse antes que expr2 (y, si expr2 contiene expresiones propias, también deben evaluarse de izquierda a derecha).

El PEP en dict-comps además establece que lo siguiente debe ser semánticamente equivalente:

La semántica de las comprensiones de dictado se puede demostrar realmente en stock Python 2.2, pasando una comprensión de lista al constructor de diccionario incorporado:

>>> dict([(i, chr(65+i)) for i in range(4)])

es semánticamente equivalente a:

>>> {i : chr(65+i) for i in range(4)}

donde la tupla (i, chr(65+i)) se evalúa de izquierda a derecha como se esperaba.

Cambiar esto para comportarse de acuerdo con las reglas de las expresiones crearía una inconsistencia en la creación de dict s, por supuesto. Las comprensiones del diccionario y un bucle for con asignaciones dan como resultado un orden de evaluación diferente, pero está bien, ya que solo sigue las reglas.

Aunque este no es un problema importante, se debe arreglar (ya sea la regla de evaluación o los documentos) para desambiguar la situación.

* Internamente , esto resulta en una asignación a un objeto de diccionario pero, esto no debería romper el comportamiento que deberían tener las expresiones. Los usuarios tienen expectativas sobre cómo deben comportarse las expresiones como se indica en el manual de referencia.

Como los demás respondedores señalaron, ya que realiza una acción de mutación en una de las expresiones, descarta cualquier información sobre lo que se evalúa primero; El uso de llamadas print , como lo hizo Duncan, arroja luz sobre lo que se hace.

Una función para ayudar a mostrar la discrepancia:

def printer(val): print(val, end='' '') return val

Visualización del diccionario (fijo):

>>> d = {printer(0): printer(1), printer(2): printer(3)} 0 1 2 3

(Impar) la comprensión del diccionario:

>>> t = (0, 1), (2, 3) >>> d = {printer(i):printer(j) for i,j in t} 1 0 3 2

y sí, esto se aplica específicamente para C Python. No tengo conocimiento de cómo otras implementaciones evalúan este caso específico (aunque todas deben ajustarse al Manual de referencia de Python).

Excavar en la fuente siempre es agradable (y también encuentras comentarios ocultos que describen el comportamiento), así que echemos un vistazo en compiler_sync_comprehension_generator del archivo compile.c :

case COMP_DICTCOMP: /* With ''d[k] = v'', v is evaluated before k, so we do the same. */ VISIT(c, expr, val); VISIT(c, expr, elt); ADDOP_I(c, MAP_ADD, gen_index + 1); break;

Esto podría parecer una buena razón y, si se juzga como tal, debería ser clasificado como un error de documentación, en su lugar.

En una prueba rápida que hice, cambiando estas declaraciones ( VISIT(c, expr, elt); visitándome primero) y cambiando el orden correspondiente en MAP_ADD (que se usa para dict-comps):

TARGET(MAP_ADD) { PyObject *value = TOP(); # was key PyObject *key = SECOND(); # was value PyObject *map; int err;

resultados en la evaluación que uno esperaría según los documentos, con la clave evaluada antes del valor. (No para sus versiones asíncronas, eso es otro conmutador requerido).

Dejaré un comentario sobre el problema y lo actualizaré cuando alguien vuelva a llamarme.

Cuestión creada 29652 - Repare el orden de evaluación de las claves / valores en versiones de dictado en el rastreador. Se actualizará la pregunta cuando se avanza en ella.