Разница между dict.clear () и назначением {} в Python

167

В python есть ли разница между вызовом clear()и назначением {}словаря? Если да, что это? Пример:

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

Marcin
источник
Интересно, имеет ли это значение в части сбора мусора. Я чувствую, что .clear () должен быть лучше для системы памяти.
Ксавье Николет

Ответы:

285

Если у вас есть другая переменная, также ссылающаяся на тот же словарь, есть большая разница:

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

Это связано с тем, что назначение d = {}создает новый пустой словарь и присваивает его dпеременной. Это оставляет d2указание на старый словарь с предметами, все еще в нем. Однако, d.clear()очищает и тот же словарь , что dи d2как точку.

Грег Хьюгилл
источник
7
Спасибо. Это имеет смысл. Я все еще должен привыкнуть к мышлению, которое = создает ссылки в python ...
Marcin
15
= копирует ссылки на имена. В Python нет переменных, только объекты и имена.
Цот
17
Хотя ваше утверждение «без переменных» педантически верно, здесь оно не очень полезно. Пока в документации по языку Python все еще говорится о «переменных», я все еще буду использовать термин: docs.python.org/reference/datamodel.html
Грег Хьюгилл
9
Я нашел комментарий tzot полезным для корректировки моего мышления об именах, переменных и типах копий. Называть это педантичным может быть вашим мнением, но я считаю, что это несправедливо суровое суждение.
cfwschmidt
1
Также clear () не уничтожает удаленный объект в dict, на который может ссылаться кто-то еще.
Лоренцо Белли
31

d = {}создаст новый экземпляр для, dно все другие ссылки будут по-прежнему указывать на старое содержимое. d.clear()сбросит содержимое, но все ссылки на один и тот же экземпляр все равно будут правильными.

Мишель
источник
21

В дополнение к различиям, упомянутым в других ответах, есть также разница в скорости. d = {} более чем в два раза быстрее:

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
источник
9
Это не совсем правильный тест скорости для всех случаев, так как dict пуст. Я думаю, что создание большого dict (или, по крайней мере, некоторого контента) приведет к гораздо меньшей разнице в производительности ... плюс я подозреваю, что сборщик мусора может добавить немного собственного вреда к d = {} (?)
Rafe
3
@Rafe: Я думаю, что смысл в том, что если мы знаем, что никакая другая переменная не указывает на словарь d, то установка d = {}должна быть более быстрой, поскольку очистка целого может быть оставлена ​​для сборщика мусора на потом.
ViFI
8

В качестве иллюстрации для вещей, уже упомянутых ранее:

>>> a = {1:2}
>>> id(a)
3073677212L
>>> a.clear()
>>> id(a)
3073677212L
>>> a = {}
>>> id(a)
3073675716L
MAXP
источник
Это показывает, что .clearизменяет объект, но `= {}` создает новый объект.
wizzwizz4
7

В дополнение к ответу @odano, кажется, использовать d.clear()быстрее, если вы хотели бы очистить голос много раз.

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)

Результат:

20.0367929935
19.6444659233
lastland
источник
4
Я не уверен, что разница значительна. В любом случае, на моей машине результаты противоположные!
Аристид
7

Методы мутации всегда полезны, если исходный объект не находится в области видимости:

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

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

Повторное назначение словаря создаст новый объект и не изменит исходный.

Каролий хорват
источник
4

Одна вещь, не упомянутая, является вопросами определения объема. Не очень хороший пример, но вот случай, когда я столкнулся с проблемой:

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

Решение состоит в том, чтобы заменить c_kwargs = {}сc_kwargs.clear()

Если кто-то придумает более практичный пример, не стесняйтесь редактировать этот пост.

Ponkadoodle
источник
global c_kwargsвероятно, тоже будет работать нет? Хотя, вероятно global, не самая лучшая вещь для использования.
Фантабол
3
Использование @fantabolous globalприведет к тому, что функция будет вести себя по-разному - все вызовы conf_decorator будут совместно использовать одну и ту же переменную c_kwargs. Я считаю, что Python 3 добавил nonlocalключевое слово для решения этой проблемы, и это будет работать.
Ponkadoodle
1

Кроме того, иногда экземпляр dict может быть подклассом dict ( defaultdictнапример). В этом случае использование clearявляется предпочтительным, поскольку нам не нужно запоминать точный тип dict, а также избегать дублирования кода (связывание строки очистки с линией инициализации).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
Tzach
источник