2012-01-23 9 views
12

en Python, escribí esta función para enseñar a mí mismo cómo **kwargs obras en Python:En Python, ¿qué determina el orden al iterar a través de kwargs?

def fxn(a1, **kwargs): 
    print a1 
    for k in kwargs: 
     print k, " : ", kwargs[k] 

Entonces llamaron a esta función con

fxn(3, a2=2, a3=3, a4=4) 

Aquí era la salida que mi intérprete de Python impreso:

3 
a3 : 3 
a2 : 2 
a4 : 4 

¿Por qué el bucle for imprime el valor de a3 antes que el de a2 a pesar de que primero alimenté a2 en mi función?

Respuesta

24

kwargs es un diccionario. Los diccionarios no están ordenados, simplemente, el orden no está especificado y hay un detalle de implementación. Mirar por debajo del capó mostrará que el orden varía enormemente dependiendo de los valores hash de los elementos, el orden de inserción, etc. por lo que es mejor que no confíe en nada relacionado con él.

1

kwargs es un diccionario, en Python estos no están ordenados por lo que el resultado es esencialmente (pseudo-) aleatorio.

10

Este es un diccionario. Y, como se menciona en la documentación, diccionario tiene ningún orden (de http://docs.python.org/tutorial/datastructures.html#dictionaries):

Lo mejor es pensar en un diccionario como un conjunto desordenado de pares clave: valor, con el requisito de que las claves son únicos (dentro de un diccionario).

Pero se puede que sea procesada en algún orden, así:

  • usando sorted():

    def fxn(a1, **kwargs): 
        print a1 
        for k in sorted(kwargs): # notice "kwargs" replaced by "sorted(kwargs)" 
         print k, " : ", kwargs[k] 
    
  • o utilizando OrderedDict tipo (que puede pasar OrderedDict objeto como parámetro que contiene todos los pares clave-valor):

    from collections import OrderedDict 
    
    def fxn(a1, ordkwargs): 
        print a1 
        for k in ordkwargs: 
         print k, " : ", ordkwargs[k] 
    
    fxn(3, OrderedDict((('a2',2), ('a3',3), ('a4',4)))) 
    
+0

Véase mi respuesta con respecto a OrderedDict – PaulMcG

+0

@PaulMcGuire:... Ver mi comentario a tu respuesta – Tadeck

+0

¿No lo escribirías como 'fxn (3, ** OrderedDict (etc ...'? Oh, espera, veo que cambiaste la firma para tomar un dict, no un conjunto de argumentos de palabras clave Si va a cambiar la firma, entonces también podría pasar una lista de sus tuplas de valor clave. Pero el OP realmente quería iterar sobre ** kwargs. – PaulMcG

2

Desde kwargs es una pitón diccionario, que se implementa como un hash table, su pedido no se conserva y es efectivamente aleatoria.

En realidad, como una corrección a un reciente security issue en muchos lenguajes de programación, en el futuro el orden puede incluso cambiar entre las invocaciones de su programa (invocaciones del intérprete de Python).

+2

No es aleatorio, de hecho, es totalmente determinado. Simplemente no es fácilmente predecible, ya que depende del hash. –

+1

Tienes razón por supuesto. Esa es la razón por la que dije "efectivamente al azar", para indicarle al lector que no debería intentar adivinarlo. En una nota al margen, casi todo lo que hace una computadora es determinado, pero puede no ser fácilmente predecible :) – spatz

4

La desafortunada ironía es que el dict-ficación de kwargs ** significa que el siguiente no funciona (al menos no de la manera que uno esperaría):

od = OrderedDict(a=1, b=2, c=3) 

Dado que los argumentos de palabras claves se construyen por primera vez en un dict no ordenado, no puede depender de que se inserten en el OrderedDict en el orden en que se enumeran.:(

+1

Pero lo siguiente: 'od = OrderedDict ((('a', 1), ('b', 2), (' c ', 3))) '. Así que básicamente '** kwargs', porque es un' dict', no tendrá ningún orden, pero siempre se puede pasar 'OrderedDict' como parámetro, y tendrá el orden que desee. – Tadeck

3

Esto finalmente se ha introducido en el 3.6 release El **kwargs es ahora un OrderedDict, por lo tanto, se preserva el orden de los argumentos de palabras clave

Python 3.6.0 (default, Jan 13 2017, 13:27:48) 
>>> def print_args(**kwargs): 
...  print(kwargs.keys()) 
... 
>>> print_args(first=1, second=2, third=3) 
dict_keys(['first', 'second', 'third']) 
+0

'** kwargs' no son un' OrderedDict' en Python 3.6. Continúan siendo un 'dict', pero ahora' dict' está ordenado. –

Cuestiones relacionadas