C'è una differenza tra usare un letterale dict e un costruttore dict?


204

Usando PyCharm, ho notato che offre di convertire un dict letterale :

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

in un costruttore di dict :

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

Questi diversi approcci differiscono in qualche modo significativo?

(Durante la stesura di questa domanda ho notato che l'utilizzo dict()sembra impossibile specificare un tasto numerico ... d = {1: 'one', 2: 'two'}è possibile, ma, ovviamente, dict(1='one' ...)non lo è. Nient'altro?)


4
dict()prende un elenco di coppie chiave-valore oltre a consentire i parametri con nome, quindi può essere utilizzato per creare qualsiasi tipo di dict, non solo con la sintassi che stai utilizzando. Probabilmente non vale nemmeno nulla che ci sia stato un bug ( youtrack.jetbrains.net/issue/PY-2512 ) in pyCharm proprio a causa di ciò che hai scoperto, che è stato risolto).
Wooble,

1
correlato: stackoverflow.com/questions/5790860/... (riassunto: il comportamento di PyCharm è più lento e più brutto)
Wooble

1
Apparentemente CPython 2.7 dict () è più lento (6 volte più lento?). Vedi: doughellmann.com/2012/11/… In ogni caso sto iniziando a preferire comunque la sintassi del costruttore poiché trovo più facile digitare e spostare il codice tra dicts e chiamate di funzione.
David Wheaton,

2
Non dimenticare gli spazi: non puoi creare chiavi che contengono spazi usando il secondo modo. Il primo modo, tuttavia, può prendere qualsiasi stringa, non gli importerà. Lo stesso vale per Unicode, ovviamente.
CamilB,

2
In Python 2, il dict(abc = 123)costruttore produce un dizionario con chiavi a stringa di byte 'abc', il che può sorprendere se si stanno utilizzando unicode_literalse si aspettano che le chiavi del dizionario siano unicode u'abc'. Vedi stackoverflow.com/questions/20357210/… .
Li-aung Yip,

Risposte:


116

Penso che tu abbia sottolineato la differenza più ovvia. A parte quello,

il primo non ha bisogno di dictcercare, il che dovrebbe renderlo un po 'più veloce

il secondo cerca dictdentro locals()e poi globals()e trova l'integrato, quindi puoi cambiare il comportamento definendo un locale chiamato dictad esempio anche se non riesco a pensare da nessuna parte che sarebbe una buona idea a parte forse durante il debug


4
Un esempio di cui una chiamata dict locale potrebbe essere utile: stackoverflow.com/a/7880276/313113
Bitek

Credo anche che l'uso di dict () costruirà prima un dict per gli argomenti a dict () e quindi creerà un secondo dict per la creazione dell'istanza effettiva di dict. Le parentesi graffe creano l'istanza dict in un solo passaggio.
NeilG

56

Literal è molto più veloce, poiché utilizza i codici operativi BUILD_MAP e STORE_MAP ottimizzati anziché CALL_FUNCTION generici:

> 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

10
@Ned: il più delle volte per la maggior parte degli utenti non importa affatto, ma ci sono situazioni in cui vengono creati milioni o miliardi di questi e una velocità 2x è significativa.
Fooz,

5
@MrFooz: ci sono situazioni del genere. Penso che scoprirai che il 99,9% delle persone che fanno micro-cronometri non si trova in quelle situazioni.
Ned Batchelder il

29
@Ned È pertinente in un thread che chiede quale sia più veloce però.
Elliott il

11
@Elliot L'OP non ha chiesto quale sia più veloce.
Tom Ferguson,

5
Se stai producendo milioni di dicts o un dict con milioni di chiavi, dai letterali dict nella tua fonte, stai sbagliando.
jwg

41

Sembrano praticamente uguali su Python 3.2.

Come ha sottolineato gnibbler, il primo non ha bisogno di dictcercare, il che dovrebbe renderlo un po 'più veloce.

>>> 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

Si noti che in alcune implementazioni, questo non è in realtà un "pochino", più come un fattore 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

Questi due approcci producono dizionari identici, tranne, come hai notato, in cui le regole lessicali di Python interferiscono.

I letterali del dizionario sono dizionari un po 'più ovvi e puoi creare qualsiasi tipo di chiave, ma devi citare i nomi delle chiavi. D'altra parte, è possibile utilizzare le variabili per le chiavi se è necessario per qualche motivo:

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

Il dict()costruttore offre maggiore flessibilità a causa della varietà di forme di input necessarie. Ad esempio, puoi fornirlo con un iteratore di coppie e le tratterà come coppie chiave / valore.

Non ho idea del perché PyCharm si offrirebbe di convertire un modulo in un altro.


2
Beh, immagino che PyCharm stia solo cercando di essere molto gentile. Proprio come sembra sempre offrire di convertire stringhe a virgoletta singola in virgolette doppie - senza motivo apparente.
Maligree

1
Devi solo citare le tue chiavi se le tue chiavi sono stringhe. Potrebbero anche essere facilmente tuple di frozenset di galleggianti, anche se questo potrebbe diventare un po 'brutto.
Wooble,

7

Una grande differenza con python 3.4 + pycharm è che il costruttore dict () produce un messaggio di "errore di sintassi" se il numero di chiavi supera 256.

Preferisco usare letteralmente il dict.


3
Non è solo Python 3.4. Questo perché CPython <3.7 ha un numero massimo di 255 argomenti letterali passati a un callable. ( Stackoverflow.com/a/8932175/2718295 )
cowbert

6

Dal tutorial di Python 2.7:

Una coppia di parentesi graffe crea un dizionario vuoto: {}. Posizionando un elenco separato da virgole di coppie chiave: valore all'interno delle parentesi graffe aggiunge la chiave iniziale: coppie valore al dizionario; questo è anche il modo in cui i dizionari sono scritti in uscita.

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

Mentre:

Il costruttore dict () crea dizionari direttamente dagli elenchi di coppie chiave-valore archiviate come tuple. Quando le coppie formano un modello, la comprensione dell'elenco può specificare in modo compatto l'elenco di valori-chiave.

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)))

Quando le chiavi sono stringhe semplici, a volte è più facile specificare le coppie usando argomenti di parole chiave:

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

Quindi sia {} che dict () producono dizionario ma forniscono modi leggermente diversi di inizializzazione dei dati del dizionario.


3

Trovo il dict letterale d = {'one': '1'}molto più leggibile, i tuoi dati definitivi, piuttosto che assegnare valori alle cose e inviarli al dict()costruttore.

D'altra parte, ho visto persone distorte il dict letterale come d = {'one', '1'}nel moderno pitone 2.7+ creerà un set.

Nonostante ciò, preferisco comunque usare letteralmente l'insieme perché penso che sia la sua preferenza personale più leggibile, suppongo.


Dimentico regolarmente che setesiste la sintassi letterale per s. Vorrei che ci fosse una sintassi letterale per i dadi ordinati ... abbastanza sicuro di usarli più spesso dei set.
ArtOfWarfare il

2

il valore letterale dict () è utile quando si copiano incollando valori da qualcos'altro (nessuno python) Ad esempio un elenco di variabili di ambiente. se avessi un file bash, diciamo

FOO='bar'
CABBAGE='good'

puoi facilmente incollare quindi in un dict()letterale e aggiungere commenti. Inoltre, rende più facile fare il contrario, copiare in qualcos'altro. Considerando che la {'FOO': 'bar'}sintassi è piuttosto unica per Python e Json. Quindi, se usi molto json, potresti voler usare {}letterali con virgolette doppie.


2

Non esiste alcun letterale dict per creare classi ereditate da dict, classi dict personalizzate con metodi aggiuntivi. In tal caso, è necessario utilizzare il costruttore della classe dict personalizzata, ad esempio:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})

0

Considera anche il fatto che i token corrispondenti per gli operatori non possono essere utilizzati nella sintassi del costruttore, ovvero chiavi dasherizzate.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.