Diferença entre dict.clear () e atribuir {} em Python

167

Em python, existe uma diferença entre chamar clear()e atribuir {}a um dicionário? Se sim, o que é isso? Exemplo:

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

Marcin
fonte
Gostaria de saber se isso faz diferença na parte da coleta de lixo. Eu sinto que .clear () deve ser mais agradável para o sistema de memória.
Xavier Nicollet

Respostas:

285

Se você tiver outra variável também se referindo ao mesmo dicionário, há uma grande diferença:

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

Isso ocorre porque a atribuição d = {}cria um novo dicionário vazio e o atribui à dvariável. Isso deixa d2apontar para o dicionário antigo com itens ainda nele. No entanto, d.clear()limpa o mesmo dicionário que de d2ambos apontam para.

Greg Hewgill
fonte
7
Obrigado. Isso faz sentido. Eu ainda tenho que me acostumar com a mentalidade de que = cria referências em python ...
Marcin
15
= copia referências para nomes. Não há variáveis ​​em python, apenas objetos e nomes.
tzot 16/12/08
17
Embora sua declaração "sem variáveis" seja verdadeira pedanticamente, não é realmente útil aqui. Enquanto a documentação linguagem Python ainda fala sobre "variáveis", eu ainda vou usar o termo: docs.python.org/reference/datamodel.html
Greg Hewgill
9
Achei o comentário de tzot útil para ajustar meu pensamento sobre nomes, variáveis ​​e tipos de cópias. Chamar isso de pedante pode ser sua opinião, mas acho que é um julgamento injustamente duro.
cfwschmidt
1
Também clear () não destrói o objeto removido no dict que ainda pode ser referenciado por outra pessoa.
Lorenzo Belli 26/01
31

d = {}criará uma nova instância para, dmas todas as outras referências ainda apontarão para o conteúdo antigo. d.clear()redefinirá o conteúdo, mas todas as referências à mesma instância ainda estarão corretas.

Michel
fonte
21

Além das diferenças mencionadas em outras respostas, também há uma diferença de velocidade. d = {} é duas vezes mais 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
fonte
9
Este não é realmente um teste de velocidade válido para todos os casos, pois o dict está vazio. Eu acho que fazer um ditado grande (ou pelo menos algum conteúdo) traria uma diferença de desempenho muito menor ... mais eu suspeito que o coletor de lixo possa adicionar um pouco de sua própria mágoa a d = {} (?)
Rafe
3
@ Rafe: Eu acho que o ponto é que, se sabemos que nenhuma outra variável está apontando para o dicionário d, a configuração d = {}deve ser mais rápida, pois a limpeza de todo pode ser deixada para o Garbage Collector para mais tarde.
ViFI
8

Como ilustração para as coisas já mencionadas anteriormente:

>>> a = {1:2}
>>> id(a)
3073677212L
>>> a.clear()
>>> id(a)
3073677212L
>>> a = {}
>>> id(a)
3073675716L
maxp
fonte
Isso mostra que .clearmodifica o objeto, mas `= {}` cria um novo objeto.
precisa saber é o seguinte
7

Além da resposta do @odano, parece que o uso d.clear()é mais rápido se você quiser limpar o ditado por muitas vezes.

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)

O resultado é:

20.0367929935
19.6444659233
lastland
fonte
4
Não sei se a diferença é significativa. De qualquer forma, na minha máquina, os resultados são opostos!
Aristide
7

Os métodos de mutação são sempre úteis se o objeto original não estiver no escopo:

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

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

Reatribuir o dicionário criaria um novo objeto e não modificaria o original.

Karoly Horvath
fonte
4

Uma coisa não mencionada são os problemas de escopo. Não é um ótimo exemplo, mas aqui está o caso em que me deparei com o 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

A solução é substituir c_kwargs = {}porc_kwargs.clear()

Se alguém pensar em um exemplo mais prático, fique à vontade para editar esta postagem.

Ponkadoodle
fonte
global c_kwargsprovavelmente também funcionaria não? Embora provavelmente globalnão seja a melhor coisa para se usar muito.
precisa saber é o seguinte
3
O uso de @fantabolous globalfaria a função se comportar de maneira diferente - todas as chamadas para conf_decorator compartilhariam a mesma variável c_kwargs. Acredito que o Python 3 adicionou a nonlocalpalavra-chave para resolver esse problema, e isso funcionaria.
Ponkadoodle
1

Além disso, às vezes a instância dict pode ser uma subclasse de dict ( defaultdictpor exemplo). Nesse caso, clearé preferível usar , pois não precisamos lembrar o tipo exato do ditado e também evitar código duplicado (acoplando a linha de limpeza com a linha de inicialização).

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