Différence entre dict.clear () et assigning {} en Python


167

En python, y a-t-il une différence entre appeler clear()et assigner {}à un dictionnaire? Si oui, qu'est ce que c'est? Exemple:

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


Je me demande si cela fait une différence sur la partie ramassage des ordures. Je pense que .clear () devrait être plus agréable pour le système de mémoire.
Xavier Nicollet

Réponses:


285

Si vous avez une autre variable faisant également référence au même dictionnaire, il y a une grande différence:

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

En effet, l'affectation d = {}crée un nouveau dictionnaire vide et l'affecte à la dvariable. Cela laisse d2pointer vers l'ancien dictionnaire avec des éléments toujours dedans. Cependant, d.clear()efface le même dictionnaire vers lequel det les d2deux pointent.


7
Merci. C'est logique. Je dois encore m'habituer à l'état d'esprit qui = crée des références en python ...
Marcin

15
= copie les références aux noms. Il n'y a pas de variables en python, seulement des objets et des noms.
tzot

17
Bien que votre déclaration «pas de variables» soit véridiquement vraie, elle n'est pas vraiment utile ici. Tant que la documentation du langage Python parle encore de «variables», je vais toujours utiliser le terme: docs.python.org/reference/datamodel.html
Greg Hewgill

9
J'ai trouvé le commentaire de tzot utile pour ajuster ma réflexion sur les noms, les variables et les types de copies. Le qualifier de pédant peut être votre opinion, mais je trouve que c'est un jugement injustement sévère.
cfwschmidt

1
De plus, clear () ne détruit pas l'objet supprimé dans le dict qui peut encore être référencé par quelqu'un d'autre.
Lorenzo Belli

31

d = {}créera une nouvelle instance pour dmais toutes les autres références pointeront toujours vers l'ancien contenu. d.clear()réinitialisera le contenu, mais toutes les références à la même instance seront toujours correctes.


21

En plus des différences mentionnées dans d'autres réponses, il y a aussi une différence de vitesse. d = {} est deux fois plus rapide:

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
Ce n'est pas vraiment un test de vitesse valide pour tous les cas puisque le dict est vide. Je pense que faire un grand dict (ou au moins un contenu) entraînerait une différence de performances beaucoup plus petite ... et je soupçonne que le ramasse-miettes pourrait ajouter un peu de son propre mal à d = {} (?)
Rafe

3
@Rafe: Je pense que le problème est que si nous savons qu'aucune autre variable ne pointe vers le dictionnaire d, le réglage d = {}devrait être plus rapide car le nettoyage de l'ensemble peut être laissé à Garbage Collector pour plus tard.
ViFI

8

Pour illustrer les choses déjà mentionnées précédemment:

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

Cela montre que cela .clearmodifie l'objet mais `= {}` crée un nouvel objet.
wizzwizz4

7

En plus de la réponse de @odano, il semble que l'utilisation d.clear()soit plus rapide si vous souhaitez effacer le dict plusieurs fois.

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)

Le résultat est:

20.0367929935
19.6444659233

4
Je ne suis pas sûr que la différence soit significative. Bref, sur ma machine, les résultats sont opposés!
Aristide

7

Les méthodes de mutation sont toujours utiles si l'objet d'origine n'est pas dans la portée:

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

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

Réaffecter le dictionnaire créerait un nouvel objet et ne modifierait pas l'original.


4

Une chose qui n'a pas été mentionnée concerne les problèmes de portée. Ce n'est pas un bon exemple, mais voici le cas où j'ai rencontré le problème:

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 solution est de remplacer c_kwargs = {}parc_kwargs.clear()

Si quelqu'un imagine un exemple plus pratique, n'hésitez pas à modifier ce message.


global c_kwargsfonctionnerait probablement aussi non? Bien que ce globalne soit probablement pas la meilleure chose à utiliser beaucoup.
fantastique

3
@fantabolous utiliser globalferait la fonction se comporter différemment - tous les appels à conf_decorator partageraient alors la même variable c_kwargs. Je pense que Python 3 a ajouté le nonlocalmot - clé pour résoudre ce problème, et cela fonctionnerait.
Ponkadoodle

1

De plus, parfois l'instance de dict peut être une sous-classe de dict ( defaultdictpar exemple). Dans ce cas, l'utilisation clearest préférable, car nous n'avons pas à nous souvenir du type exact du dict, et évitez également le code en double (couplage de la ligne de compensation avec la ligne d'initialisation).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.