2009-03-19 65 views
49

Digamos que quiero hacer un diccionario. Lo llamaremos d. ¡Pero hay múltiples formas de inicializar un diccionario en Python! Por ejemplo, podría hacer esto:¿Cuál es la diferencia entre dict() y {}?

d = {'hash': 'bang', 'slash': 'dot'} 

O podría hacer esto:

d = dict(hash='bang', slash='dot') 

O esto, curiosamente:

d = dict({'hash': 'bang', 'slash': 'dot'}) 

O esto:

d = dict([['hash', 'bang'], ['slash', 'dot']]) 

Y una multitud de formas con el dict() función. Entonces, obviamente, una de las cosas que dict() proporciona es flexibilidad en la sintaxis y la inicialización. Pero eso no es lo que estoy preguntando.

Dije que debía hacer d solo un diccionario vacío. ¿Qué sucede detrás de las escenas del intérprete de Python cuando hago d = {} contra d = dict()? ¿Es simplemente dos formas de hacer lo mismo? ¿El uso de {} tiene adicional llamada de dict()? ¿Tiene uno (incluso insignificante) más sobrecarga que el otro? Si bien la pregunta no tiene importancia, es una curiosidad que me gustaría haber respondido.

+0

me encontré con el lote explicación más clara aquí: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of -using-dict-instead-of-in-cpython-2-7-2/ – theBuzzyCoder

Respuesta

61
>>> def f(): 
...  return {'a' : 1, 'b' : 2} 
... 
>>> def g(): 
...  return dict(a=1, b=2) 
... 
>>> g() 
{'a': 1, 'b': 2} 
>>> f() 
{'a': 1, 'b': 2} 
>>> import dis 
>>> dis.dis(f) 
    2   0 BUILD_MAP    0 
       3 DUP_TOP    
       4 LOAD_CONST    1 ('a') 
       7 LOAD_CONST    2 (1) 
      10 ROT_THREE   
      11 STORE_SUBSCR   
      12 DUP_TOP    
      13 LOAD_CONST    3 ('b') 
      16 LOAD_CONST    4 (2) 
      19 ROT_THREE   
      20 STORE_SUBSCR   
      21 RETURN_VALUE   
>>> dis.dis(g) 
    2   0 LOAD_GLOBAL    0 (dict) 
       3 LOAD_CONST    1 ('a') 
       6 LOAD_CONST    2 (1) 
       9 LOAD_CONST    3 ('b') 
      12 LOAD_CONST    4 (2) 
      15 CALL_FUNCTION   512 
      18 RETURN_VALUE   

dict() es aparentemente algo C incorporado. Una persona realmente inteligente o dedicada (no yo) podría consultar la fuente del intérprete y contarle más. Solo quería mostrar dis.dis. :)

+1

Lamentablemente, nadie podría decir lo que hay detrás de 'g' debido a' CALL_FUNCTION 512' ... –

+2

Echa un vistazo a este bonito artículo de Doug Hellmann: http://doughellmann.com/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – ezdazuzena

+1

Nueva python ha hecho ' {'a': 1, 'b': 2} 'más eficiente. p.ej. en python 2.7.10, el desmontaje reemplaza al 'ROT_THREE; STORE_SUBSCR; Instrucciones DUP_TOP' con 'STORE_MAP'. En Python 3.5.1 se deshace de todos ellos y solo tiene un 'BUILD_MAP'. – danio

31

En lo que respecta al rendimiento:

>>> from timeit import timeit 
>>> timeit("a = {'a': 1, 'b': 2}") 
0.424... 
>>> timeit("a = dict(a = 1, b = 2)") 
0.889... 
+0

Obtengo un resultado similar para el diccionario vacío: 0.1usec para 'x = {}' frente a 0.3usec para 'x = dict()'. –

+0

Sí; las llamadas a funciones son costosas. Pero esa forma es más versátil, es decir, dict (zip ... – DNS

+0

exactamente. Las llamadas a funciones son unas 80-100 veces más costosas en Python, que en C. – vartec

8

Básicamente, {} es la sintaxis y se maneja a nivel de lenguaje y código de bytes. dict() es simplemente otro built-in con una sintaxis de inicialización más flexible. Tenga en cuenta que dict() solo se agregó en el medio de la serie 2.x.

5

Actualización: gracias por las respuestas. Se ha eliminado la especulación sobre copy-on-write.

Otra diferencia entre {} y dictdict es que siempre asigna un nuevo diccionario (incluso si los contenidos son estáticos), mientras que {} no siempre hacerlo (véase mgood's answer de cuándo y por qué):

def dict1(): 
    return {'a':'b'} 

def dict2(): 
    return dict(a='b') 

print id(dict1()), id(dict1()) 
print id(dict2()), id(dict2()) 

produce:

 
$ ./mumble.py 
11642752 11642752 
11867168 11867456 

no estoy sugiriendo que intenta sacar provecho de esto o no, que depe encuentra en la situación particular, simplemente lo señala. (También es probable que sea evidente en el disassembly si comprende los códigos de operación).

+2

Eso no es lo que está sucediendo aquí. {} todavía está asignando un nuevo dict (de lo contrario sería muy malo), pero el hecho de que no lo mantengas vivo significa que el ID puede reutilizarse después de que muera (tan pronto como se imprima el primer dict). – Brian

+0

Sospecho que la llamada a la función está actuando de manera diferente porque está agitando un poco más el espacio id(), por lo que se obtiene una identificación diferente en la segunda llamada. – Brian

+0

Creo que la respuesta de mgood aclaró las cosas; He actualizado mi entrada. –

24

@Jacob: Hay una diferencia en cómo se asignan los objetos, pero no son de escritura por escritura. Python asigna una "lista libre" de tamaño fijo donde puede asignar rápidamente objetos de diccionario (hasta que se llena). Los diccionarios asignados a través de la sintaxis {} (o una llamada C al PyDict_New) pueden provenir de esta lista gratuita.Cuando el diccionario ya no se referencia, se devuelve a la lista libre y ese bloque de memoria se puede reutilizar (aunque los campos se restablecen primero).

Este primer diccionario inmediatamente se volvió a la lista libre, y el próximo será volver a utilizar su espacio de memoria:

>>> id({}) 
340160 
>>> id({1: 2}) 
340160 

Si se mantiene una referencia, el siguiente diccionario vendrán de la siguiente ranura libre:

>>> x = {} 
>>> id(x) 
340160 
>>> id({}) 
340016 

Pero podemos suprimir la referencia a ese diccionario y libre de su ranura de nuevo:

>>> del x 
>>> id({}) 
340160 

Dado que la sintaxis {} se maneja en código de bytes, puede usar esta optimización mencionada anteriormente. Por otro lado, dict() se maneja como un constructor de clase regular y Python usa el asignador de memoria genérico, que no sigue un patrón fácilmente predecible como la lista gratuita anterior.

Además, mirando compile.c desde Python 2.6, con la sintaxis {} parece pre-dimensionar la tabla hash basada en la cantidad de elementos que almacena que se conoce en el tiempo de análisis.

3

dict() se utiliza cuando se desea crear un diccionario de un iterable, como:

dict(generator which yields (key,value) pairs) 
dict(list of (key,value) pairs) 
+1

Sí. Y los casos que pueden usar '{...}' deberían hacerlo porque es más directo y más rápido que una llamada al constructor 'dict()' (las llamadas a funciones son costosas, en Python, y por qué llamar a una función que solo devuelve algo que se puede construir directamente a través de la sintaxis '{...}'? – EOL

+0

Creo que {...} solo funciona en python3 – peufeu

+0

Ha estado funcionando en Python 2 desde hace bastante tiempo. – EOL

-1

el fin de crear un conjunto vacío debemos utilizar el conjunto de palabras clave antes de que es decir set() esto crea una conjunto vacío donde como en dicts sólo los soportes de flores pueden crear un vacío dict

Vamos a ir con un ejemplo

print isinstance({},dict) 
True 
print isinstance({},set) 
False 
print isinstance(set(),set) 
True 
0

el uso divertido:

def func(**kwargs): 
     for e in kwargs: 
     print(e) 
    a = 'I want to be printed' 
    kwargs={a:True} 
    func(**kwargs) 
    a = 'I dont want to be printed' 
    kwargs=dict(a=True) 
    func(**kwargs) 

de salida:

I want to be printed 
a 
+1

No estoy seguro si quieres decir gracioso como en 'lol' o gracioso como en 'error'. Incluya algún tipo de comentario o explicación a esto ya que esta pregunta fue respondida hace 8 años, y esto dejará perplejos a los noobs hasta cierto punto –

Cuestiones relacionadas