Diferencia entre dict.clear () y asignar {} en Python

167

En python, ¿hay alguna diferencia entre llamar clear()y asignar {}a un diccionario? ¿Y si si, que? Ejemplo:

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

Marcin
fuente
Me pregunto si esto hace una diferencia en la parte de recolección de basura. Siento que .clear () debería ser mejor para el sistema de memoria.
Xavier Nicollet

Respuestas:

285

Si tiene otra variable que también se refiere al mismo diccionario, hay una gran diferencia:

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

Esto se debe a que la asignación d = {}crea un nuevo diccionario vacío y lo asigna a la dvariable. Esto deja d2apuntando al antiguo diccionario con elementos todavía en él. Sin embargo, d.clear()borra el mismo diccionario que dy d2ambos apuntan a.

Greg Hewgill
fuente
77
Gracias. Esto tiene sentido. Todavía tengo que acostumbrarme a la mentalidad que = crea referencias en python ...
Marcin
15
= copia referencias a nombres. No hay variables en python, solo objetos y nombres.
tzot
17
Si bien su afirmación "sin variables" es pedagógicamente verdadera, no es realmente útil aquí. Mientras la documentación del lenguaje Python siga hablando de "variables", seguiré
Greg Hewgill
9
El comentario de tzot me pareció útil para ajustar mi forma de pensar sobre los nombres, las variables y los tipos de copias. Llamarlo pedante puede ser su opinión, pero creo que es un juicio injusto y duro.
cfwschmidt
1
También clear () no destruya el objeto eliminado en el dict que aún puede ser referenciado por otra persona.
Lorenzo Belli
31

d = {}creará una nueva instancia para, dpero todas las demás referencias seguirán apuntando a los contenidos anteriores. d.clear()restablecerá el contenido, pero todas las referencias a la misma instancia seguirán siendo correctas.

Michel
fuente
21

Además de las diferencias mencionadas en otras respuestas, también hay una diferencia de velocidad. d = {} es más del doble de rápido:

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
odano
fuente
9
Esto no es realmente una prueba de velocidad válida para todos los casos ya que el dict está vacío. Creo que hacer un dict grande (o al menos algo de contenido) produciría una diferencia de rendimiento mucho menor ... además, sospecho que el recolector de basura podría agregar un poco de su propio daño a d = {} (?)
Rafe
3
@Rafe: Creo que el punto es que si sabemos que ninguna otra variable apunta al diccionario d, entonces la configuración d = {}debería ser más rápida ya que la limpieza completa puede dejarse en el recolector de basura para más adelante.
ViFI
8

Como ilustración de las cosas ya mencionadas anteriormente:

>>> a = {1:2}
>>> id(a)
3073677212L
>>> a.clear()
>>> id(a)
3073677212L
>>> a = {}
>>> id(a)
3073675716L
maxp
fuente
Esto muestra que .clearmodifica el objeto pero `= {}` crea un nuevo objeto.
wizzwizz4
7

Además de la respuesta de @odano, parece que el uso d.clear()es más rápido si desea borrar el dict muchas veces.

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)

El resultado es:

20.0367929935
19.6444659233
lastland
fuente
44
No estoy seguro de que la diferencia sea significativa. De todos modos, en mi máquina, ¡los resultados son opuestos!
Aristide
7

Los métodos de mutación siempre son útiles si el objeto original no está dentro del alcance:

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

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

Reasignar el diccionario crearía un nuevo objeto y no modificaría el original.

Karoly Horvath
fuente
4

Una cosa que no se menciona son los problemas de alcance. No es un gran ejemplo, pero aquí está el caso en el que me encontré con el 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 solución es reemplazar c_kwargs = {}conc_kwargs.clear()

Si alguien piensa en un ejemplo más práctico, siéntase libre de editar esta publicación.

Ponkadoodle
fuente
global c_kwargsprobablemente también funcionaría no? Aunque probablemente globalno sea lo mejor para usar mucho.
fantabolous
3
@fantabolous using globalharía que la función se comportara de manera diferente: todas las llamadas a conf_decorator compartirían la misma variable c_kwargs. Creo que Python 3 agregó la nonlocalpalabra clave para abordar este problema, y ​​eso funcionaría.
Ponkadoodle
1

Además, a veces la instancia dict puede ser una subclase de dict ( defaultdictpor ejemplo). En ese caso, clearse prefiere usar , ya que no tenemos que recordar el tipo exacto del dict, y también evitar el código duplicado (acoplar la línea de compensación con la línea de inicialización).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
Tzach
fuente