Differenza tra dict.clear () e l'assegnazione di {} in Python


167

In Python, c'è una differenza tra chiamare clear()e assegnare {}a un dizionario? Se si, che cos'è? Esempio:

d = {"stuff":"things"}
d.clear()   #this way
d = {}      #vs this way


Mi chiedo se questo faccia la differenza nella parte della raccolta dei rifiuti. Sento che .clear () dovrebbe essere più gentile con il sistema di memoria.
Xavier Nicollet,

Risposte:


285

Se hai un'altra variabile che fa riferimento anche allo stesso dizionario, c'è una grande differenza:

>>> d = {"stuff": "things"}
>>> d2 = d
>>> d = {}
>>> d2
{'stuff': 'things'}
>>> d = {"stuff": "things"}
>>> d2 = d
>>> d.clear()
>>> d2
{}

Questo perché l'assegnazione d = {}crea un nuovo dizionario vuoto e lo assegna alla dvariabile. Questo lascia d2puntare al vecchio dizionario con gli elementi ancora presenti. Tuttavia, d.clear()cancella lo stesso dizionario che de d2sia punto.


7
Grazie. Questo ha senso. Devo ancora abituarmi alla mentalità che = crea riferimenti in pitone ...
Marcin,

15
= copia i riferimenti ai nomi. Non ci sono variabili in Python, solo oggetti e nomi.
tzot

17
Mentre la tua affermazione "nessuna variabile" è pedanticamente vera, qui non è davvero utile. Finché la documentazione del linguaggio Python parla ancora di "variabili", userò ancora il termine: docs.python.org/reference/datamodel.html
Greg Hewgill

9
Ho trovato utile il commento di tzot per adattare il mio pensiero a nomi, variabili e tipi di copie. Definirlo pedante può essere la tua opinione, ma trovo che sia un giudizio ingiustamente duro.
cfwschmidt,

1
Anche clear () non distrugge l'oggetto rimosso nel dict a cui può fare riferimento ancora qualcun altro.
Lorenzo Belli,

31

d = {}creerà una nuova istanza per dma tutti gli altri riferimenti rimandano comunque ai vecchi contenuti. d.clear()reimposterà i contenuti, ma tutti i riferimenti alla stessa istanza saranno comunque corretti.


21

Oltre alle differenze menzionate in altre risposte, esiste anche una differenza di velocità. d = {} è due volte più veloce:

python -m timeit -s "d = {}" "for i in xrange(500000): d.clear()"
10 loops, best of 3: 127 msec per loop

python -m timeit -s "d = {}" "for i in xrange(500000): d = {}"
10 loops, best of 3: 53.6 msec per loop

9
Questo non è davvero un test di velocità valido per tutti i casi poiché il dict è vuoto. Penso che fare un grande dict (o almeno un po 'di contenuto) produrrebbe una differenza di prestazioni molto più piccola ... inoltre sospetto che il garbage collector potrebbe aggiungere un po' del suo male a d = {} (?)
Rafe

3
@Rafe: penso che il punto sia se sappiamo che nessun'altra variabile punta al dizionario d, quindi l'impostazione d = {}dovrebbe essere più veloce poiché la pulizia completa può essere lasciata a Garbage Collector per dopo.
ViFI,

8

A titolo di esempio per le cose già menzionate prima:

>>> a = {1:2}
>>> id(a)
3073677212L
>>> a.clear()
>>> id(a)
3073677212L
>>> a = {}
>>> id(a)
3073675716L

Questo dimostra che .clearmodifica l'oggetto ma `= {}` crea un nuovo oggetto.
wizzwizz4,

7

Oltre alla risposta di @odano, sembra che l'uso d.clear()sia più veloce se desideri cancellare il dict per molte volte.

import timeit

p1 = ''' 
d = {}
for i in xrange(1000):
    d[i] = i * i
for j in xrange(100):
    d = {}
    for i in xrange(1000):
        d[i] = i * i
'''

p2 = ''' 
d = {}
for i in xrange(1000):
    d[i] = i * i
for j in xrange(100):
    d.clear()
    for i in xrange(1000):
        d[i] = i * i
'''

print timeit.timeit(p1, number=1000)
print timeit.timeit(p2, number=1000)

Il risultato è:

20.0367929935
19.6444659233

4
Non sono sicuro che la differenza sia significativa. Comunque, sulla mia macchina, i risultati sono opposti!
Aristide,

7

I metodi di mutazione sono sempre utili se l'oggetto originale non rientra nell'ambito:

def fun(d):
    d.clear()
    d["b"] = 2

d={"a": 2}
fun(d)
d          # {'b': 2}

La riassegnazione del dizionario creerebbe un nuovo oggetto e non modificherebbe quello originale.


4

Una cosa non menzionata è problemi di scoping. Non è un ottimo esempio, ma ecco il caso in cui mi sono imbattuto nel problema:

def conf_decorator(dec):
    """Enables behavior like this:
        @threaded
        def f(): ...

        or

        @threaded(thread=KThread)
        def f(): ...

        (assuming threaded is wrapped with this function.)
        Sends any accumulated kwargs to threaded.
        """
    c_kwargs = {}
    @wraps(dec)
    def wrapped(f=None, **kwargs):
        if f:
            r = dec(f, **c_kwargs)
            c_kwargs = {}
            return r
        else:
            c_kwargs.update(kwargs) #<- UnboundLocalError: local variable 'c_kwargs' referenced before assignment
            return wrapped
    return wrapped

La soluzione è sostituirla c_kwargs = {}conc_kwargs.clear()

Se qualcuno pensa a un esempio più pratico, non esitare a modificare questo post.


global c_kwargsprobabilmente funzionerebbe anche no? Anche se probabilmente globalnon è la cosa migliore da usare molto.
fantabolous,

3
L'uso di @fantabolous globalfarebbe in modo che la funzione si comporti diversamente: tutte le chiamate a conf_decorator condivideranno quindi la stessa variabile c_kwargs. Credo che Python 3 abbia aggiunto la nonlocalparola chiave per risolvere questo problema e che funzionerebbe.
Ponkadoodle,

1

Inoltre, a volte l'istanza di dict potrebbe essere una sottoclasse di dict ( defaultdictad esempio). In tal caso, clearè preferibile utilizzare , poiché non dobbiamo ricordare il tipo esatto di dict ed evitare anche il codice duplicato (accoppiando la linea di cancellazione con la linea di inizializzazione).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
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.