2009-03-19 75 views
49

Quindi diciamo che voglio creare un dizionario. Lo chiameremo d. Ma ci sono molti modi per inizializzare un dizionario in Python! Per esempio, ho potuto fare questo:Qual è la differenza tra dict() e {}?

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

Oppure potrei fare questo:

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

O questo, curiosamente:

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

O questo:

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

E tutta un'altra moltitudine di modi con lo dict() funzione. Quindi, ovviamente, una delle cose che fornisce dict() è la flessibilità nella sintassi e nell'inizializzazione. Ma non è quello che sto chiedendo.

Dire che dovevo fare d solo un dizionario vuoto. Cosa succede dietro le quinte dell'interprete Python quando faccio d = {} rispetto a d = dict()? Sono semplicemente due modi per fare la stessa cosa? L'utilizzo di {} ha la chiamata aggiuntivadict()? Si ha (anche trascurabile) più in testa rispetto all'altro? Mentre la domanda è davvero del tutto insignificante, è una curiosità che mi piacerebbe avere una risposta.

+0

ho trovato il sacco spiegazione più chiara qui: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of -using-dict-instead-of-in-cpython-2-7-2/ – theBuzzyCoder

risposta

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() è apparentemente un C incorporato. Una persona davvero intelligente o dedicata (non io) potrebbe guardare la fonte dell'interprete e dirti di più. Volevo solo mettermi in mostra dis.dis. :)

+1

Purtroppo nessuno potrebbe dirti cosa c'è dietro 'g' a causa di' CALL_FUNCTION 512' ... –

+2

Dai un'occhiata a questo bel articolo di Doug Hellmann: http://doughellmann.com/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – ezdazuzena

+1

Il pitone più recente ha fatto ' {'a': 1, 'b': 2} 'più efficiente. per esempio. in python 2.7.10, lo smontaggio sostituisce 'ROT_THREE; STORE_SUBSCR; Istruzioni DUP_TOP' con 'STORE_MAP'. In python 3.5.1 si sbarazza di tutti e ha un solo 'BUILD_MAP'. – danio

31

Per quanto riguarda le prestazioni va:

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

Ottengo risultati simili per il dizionario vuoto: 0.1usec per 'x = {}' vs 0.3usec per 'x = dict()'. –

+0

Sì; le chiamate di funzione sono costose. Ma questa forma è più versatile, ad esempio dict (zip (... – DNS

+0

esatte) le chiamate di funzione sono un po '80-100 volte più costose in Python, rispetto a C. – vartec

8

In sostanza, {} è la sintassi e viene gestita a livello di lingua e bytecode. dict() è solo un altro costruito con una sintassi di inizializzazione più flessibile. Si noti che dict() è stato aggiunto solo nel mezzo della serie 2.x.

5

Aggiornamento: grazie per le risposte. Rimosse la speculazione su copy-on-write.

Un altra differenza tra {} e dict è che dict assegna sempre un nuovo dizionario (anche se i contenuti sono statici), mentre {} non sempre farlo (vedi mgood's answer per quando e perché):

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 

non sto suggerendo si tenta di approfittare di questo o no, Depe nds sulla situazione particolare, solo sottolineando. (È anche probabilmente evidente dallo disassembly se si comprendono gli opcode).

+2

Questo non è quello che sta succedendo qui. {} sta ancora assegnando un nuovo dict (altrimenti sarebbe pessimo), ma il fatto che non lo si tenga in vita significa che l'id può essere riutilizzato dopo la sua morte (non appena viene stampato il primo dict). – Brian

+0

Sospetto che la chiamata alla funzione stia agendo in modo diverso perché sta facendo girare lo spazio id() un po 'di più in modo da ottenere un id differente sulla seconda chiamata. – Brian

+0

Penso che la risposta di mgood abbia chiarito le cose; Ho aggiornato la mia voce. –

24

@Jacob: c'è una differenza nel modo in cui gli oggetti sono allocati, ma non sono copia-su-scrittura. Python alloca una "lista libera" di dimensioni fisse in cui può allocare velocemente oggetti del dizionario (fino a riempire). Dizionari allocati tramite la sintassi {} (o una chiamata C a PyDict_New) possono provenire da questa lista libera.Quando il dizionario non è più referenziato, viene restituito alla lista libera e quel blocco di memoria può essere riutilizzato (anche se i campi vengono ripristinati per primi).

Questo primo dizionario ottiene immediatamente restituito al lista libera, e la prossima sarà riutilizzare il suo spazio di memoria:

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

Se si mantiene un riferimento, il dizionario prossima arriverà dal prossimo slot libero:

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

Ma possiamo eliminare il riferimento a tale dizionario e senza slot di nuovo:

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

Poiché la sintassi {} viene gestita in byte-code, è possibile utilizzare questa ottimizzazione sopra menzionata. D'altra parte dict() viene gestito come un normale costruttore di classi e Python usa l'allocatore di memoria generico, che non segue un modello facilmente prevedibile come la lista libera sopra.

Inoltre, guardando compile.c da Python 2.6, con la sintassi {} sembra pre-dimensionare l'hashtable in base al numero di elementi memorizzati, che è noto in fase di analisi.

3

dict() viene utilizzato quando si vuole creare un dizionario da un iterabile, come:

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

Sì. E i casi che possono usare '{...}' invece dovrebbero farlo perché è più diretto e più veloce di una chiamata al costruttore 'dict()' (le chiamate di funzione sono costose, in Python, e perché chiamare una funzione che restituisce solo qualcosa che può essere costruito direttamente attraverso la sintassi '{...}'?). – EOL

+0

Credo che {...} funzioni solo in python3 – peufeu

+0

Ha funzionato in Python 2 da parecchio tempo ormai. – EOL

-1

Al fine di creare un insieme vuoto dovremmo usare la parola chiave impostata prima che cioè set() questo crea un insieme vuoto dove, come in dicts solo le staffe di fiori in grado di creare un vuoto dict

Lets go con un esempio

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

l'utilizzo divertente:

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) 

uscita:

I want to be printed 
a 
+1

Non sono sicuro se intendi divertente come in 'lol' o divertente come in 'bug'. Per favore includi una sorta di commento o spiegazione a questo come risposta a questa domanda 8 anni fa, e questo lascerà perplessi i noobs a un livello –

Problemi correlati