¿Hay alguna diferencia entre usar un literal dict y un constructor dict?

204

Usando PyCharm, noté que ofrece convertir un dict literal :

d = {
    'one': '1',
    'two': '2',
}

en un constructor dict :

d = dict(one='1', two='2')

¿Estos diferentes enfoques difieren de alguna manera significativa?

(Mientras escribía esta pregunta, noté que usarlo dict()parece imposible especificar una clave numérica ... d = {1: 'one', 2: 'two'}es posible, pero, obviamente, dict(1='one' ...)no lo es. ¿Algo más?)

maligno
fuente
44
dict()toma una lista de pares clave-valor, además de permitir parámetros con nombre, por lo que puede usarse para crear cualquier tipo de dict, solo que no con la sintaxis que está usando. Probablemente tampoco valga nada que haya un error ( youtrack.jetbrains.net/issue/PY-2512 ) en pyCharm específicamente debido a lo que descubrió, que se ha solucionado).
Wooble
1
relacionado: stackoverflow.com/questions/5790860/… (resumen: el comportamiento de PyCharm es más lento y más feo)
Wooble
1
Aparentemente, CPython 2.7 dict () es más lento (¿6 veces más lento?). Ver: doughellmann.com/2012/11/… En cualquier caso, estoy empezando a preferir la sintaxis del constructor de todos modos, ya que me resulta más fácil escribir y mover código entre dictos y llamadas a funciones.
David Wheaton
2
No olvides los espacios: no puedes crear claves que contengan espacios usando la segunda forma. Sin embargo, la primera forma puede tomar cualquier cadena, no le importará. Lo mismo se aplica a Unicode, por supuesto.
CamilB
2
En Python 2, el dict(abc = 123)constructor produce un diccionario con claves de cadena de bytes 'abc', lo que puede ser sorprendente si está utilizando unicode_literalsy espera que las claves de diccionario sean unicode u'abc'. Ver stackoverflow.com/questions/20357210/… .
Li-aung Yip

Respuestas:

116

Creo que has señalado la diferencia más obvia. Aparte de eso,

el primero no necesita buscar, lo dictque debería hacerlo un poco más rápido

el segundo mira hacia arriba dicten locals()y después globals()y los hallazgos de la orden interna, lo que puede cambiar el comportamiento mediante la definición de un local llamado dict, por ejemplo, aunque no puedo pensar en ningún lugar esto sería una idea buena, aparte de que tal vez cuando la depuración

John La Rooy
fuente
44
Un ejemplo de dónde un local llamado dict podría ser útil: stackoverflow.com/a/7880276/313113
bitek
Creo que también usar dict () primero construirá un dict para los argumentos de dict () y luego creará un segundo dict para que se cree la instancia real de dict. Braces crea la instancia dict en un solo paso.
NeilG
56

Literal es mucho más rápido, ya que utiliza códigos de operación BUILD_MAP y STORE_MAP optimizados en lugar de CALL_FUNCTION genérico:

> python2.7 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.958 usec per loop

> python2.7 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.479 usec per loop

> python3.2 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.975 usec per loop

> python3.2 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.409 usec per loop
Daniel Kluev
fuente
10
@Ned: La mayoría de las veces, para la mayoría de los usuarios, no importa en absoluto, pero hay situaciones en las que se están creando millones o miles de millones y una aceleración 2x ​​es significativa.
Sr. Fooz
55
@MrFooz: hay situaciones como esa. Creo que encontrará que el 99.9% de las personas que realizan micro-temporizaciones no se encuentran en esas situaciones.
Ned Batchelder
29
@Ned Sin embargo, es pertinente en un hilo que pregunta cuál es más rápido.
Elliott
11
@Elliot El OP no preguntó cuál es más rápido.
Tom Ferguson
55
Si está produciendo millones de dictados o un dict con millones de claves, a partir de literales dict en su fuente, lo está haciendo mal.
jwg
41

Se ven más o menos iguales en Python 3.2.

Como señaló gnibbler, el primero no necesita buscar dict, lo que debería hacerlo un poco más rápido.

>>> def literal():
...   d = {'one': 1, 'two': 2}
...
>>> def constructor():
...   d = dict(one='1', two='2')
...
>>> import dis
>>> dis.dis(literal)
  2           0 BUILD_MAP                2
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 ('one')
              9 STORE_MAP
             10 LOAD_CONST               3 (2)
             13 LOAD_CONST               4 ('two')
             16 STORE_MAP
             17 STORE_FAST               0 (d)
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
>>> dis.dis(constructor)
  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_CONST               1 ('one')
              6 LOAD_CONST               2 ('1')
              9 LOAD_CONST               3 ('two')
             12 LOAD_CONST               4 ('2')
             15 CALL_FUNCTION          512
             18 STORE_FAST               0 (d)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE
Paolo Moretti
fuente
Tenga en cuenta que en algunas implementaciones, esto no es realmente un "poquito", más como un factor de 100:$ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' "{'a': 1, 'b': 2, 'c': 3}" ....... Mean +- std dev: 1.73 ns +- 0.14 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' '{k:v for k,v in i}' ....... Mean +- std dev: 139 ns +- 10 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' 'dict(i)' ....... Mean +- std dev: 188 ns +- 16 ns
DylanYoung
13

Estos dos enfoques producen diccionarios idénticos, excepto, como se ha señalado, donde las reglas léxicas de Python interfieren.

Los literales de diccionario son un poco más obviamente diccionarios, y puede crear cualquier tipo de clave, pero necesita citar los nombres de las claves. Por otro lado, puede usar variables para claves si lo necesita por alguna razón:

a = "hello"
d = {
    a: 'hi'
    }

El dict()constructor le brinda más flexibilidad debido a la variedad de formas de entrada que toma. Por ejemplo, puede proporcionarle un iterador de pares, y los tratará como pares clave / valor.

No tengo idea de por qué PyCharm ofrecería convertir una forma a la otra.

Ned Batchelder
fuente
2
Bueno, supongo que PyCharm solo está tratando de ser más agradable. Así como siempre parece ofrecer convertir cadenas de comillas simples en comillas dobles, sin razón aparente.
maligree
1
Solo necesita citar sus claves si sus claves son cadenas. Podrían ser fácilmente tuplas de congeladores de flotadores, aunque esto podría ponerse un poco feo.
Wooble
7

Una gran diferencia con python 3.4 + pycharm es que el constructor dict () produce un mensaje de "error de sintaxis" si el número de claves excede 256.

Prefiero usar el literal dict ahora.

Michel Boiron
fuente
3
No es solo Python 3.4. Esto se debe a que CPython <3.7 tiene un número máximo de 255 argumentos literales pasados ​​a un invocable. ( stackoverflow.com/a/8932175/2718295 )
cowbert
6

Del tutorial de Python 2.7:

Un par de llaves crea un diccionario vacío: {}. Al colocar una lista separada por comas de pares clave: valor dentro de las llaves se agrega clave inicial: pares de valores al diccionario; esta es también la forma en que los diccionarios se escriben en la salida.

tel = {'jack': 4098, 'sape': 4139}
data = {k:v for k,v in zip(xrange(10), xrange(10,20))}

Mientras:

El constructor dict () construye diccionarios directamente a partir de listas de pares clave-valor almacenados como tuplas. Cuando los pares forman un patrón, las comprensiones de listas pueden especificar de manera compacta la lista clave-valor.

tel = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'jack': 4098, 'guido': 4127}
data = dict((k,v) for k,v in zip(xrange(10), xrange(10,20)))

Cuando las claves son cadenas simples, a veces es más fácil especificar pares usando argumentos de palabras clave:

dict(sape=4139, guido=4127, jack=4098)
>>>  {'sape': 4139, 'jack':4098, 'guido': 4127}

Entonces, tanto {} como dict () producen diccionario pero proporcionan formas un poco diferentes de inicialización de datos de diccionario.

Artsiom Rudzenka
fuente
3

Creo que el literal dict d = {'one': '1'}es mucho más legible, sus datos definitorios, en lugar de asignar valores a las cosas y enviarlas al dict()constructor.

Por otro lado, he visto a personas escribir mal el literal dict, ya d = {'one', '1'}que en Python 2.7+ moderno creará un conjunto.

A pesar de esto, todavía prefiero usar el literal literal en todos los sentidos porque creo que es una preferencia personal más legible, supongo.

Lee Penkman
fuente
Regularmente olvido que la sintaxis literal para sets existe. Desearía que hubiera una sintaxis literal para los dictados ordenados ... estoy bastante seguro de que los uso con más frecuencia que los conjuntos.
ArtOfWarfare
2

el literal dict () es bueno cuando copia valores de pegado de otra cosa (ninguna python) Por ejemplo, una lista de variables de entorno. si tenía un archivo bash, diga

FOO='bar'
CABBAGE='good'

puede pegar fácilmente en un dict()literal y agregar comentarios. También hace que sea más fácil hacer lo contrario, copiar en otra cosa. Mientras que la {'FOO': 'bar'}sintaxis es bastante exclusiva de python y json. Entonces, si usa mucho json, es posible que desee usar {}literales con comillas dobles.

Nick Humrich
fuente
2

No hay un literal dict para crear clases heredadas dict, clases dict personalizadas con métodos adicionales. En tal caso, se debe utilizar el constructor de clase dict personalizado, por ejemplo:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})
Dmitriy Sintsov
fuente
0

También considere el hecho de que los tokens que coinciden con los operadores no se pueden usar en la sintaxis del constructor, es decir, las teclas con guiones.

>>> dict(foo-bar=1)
File "<stdin>", line 1
SyntaxError: keyword can't be an expression

>>> {'foo-bar': 1}
{'foo-bar': 1}
Brian Whitton
fuente