Різниця між dict.clear () і призначенням {} в Python


167

Чи існує різниця між викликом clear()та призначенням {}словника у python ? Якщо так, що це? Приклад:

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


Цікаво, чи це має значення в частині збирання сміття. Я відчуваю, що .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 немає змінних, лише об'єкти та імена.
tzot

17
Хоча ваше твердження "без змінних" є педантично правдивим, воно тут не дуже корисне. Поки в документації мови Python все ще йдеться про "змінні", я все одно буду використовувати термін: docs.python.org/reference/datamodel.html
Грег Хьюгілл

9
Я вважаю коментар tzot корисним для коригування міркувань про імена, змінні та типи копій. Називати це педантичним, можливо, ваша думка, але я вважаю це несправедливо суворим рішенням.
cfwschmidt

1
Також ясно () не знищуйте вилучений об’єкт у диктаті, на який все ще може посилатися хтось інший.
Лоренцо Беллі

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

9
Це насправді не є дійсним тестом швидкості для всіх випадків, оскільки дикт порожній. Я думаю, що створення великого диктату (або принаймні деякого вмісту) призведе до значно меншої різниці в продуктивності ... плюс я підозрюю, що сміттєзбірник може додати трохи своєї власної шкоди d = {} (?)
Rafe

3
@Rafe: Я думаю, що справа полягає в тому, що якщо ми знаємо, що жодна інша змінна не вказує на словник d, то налаштування d = {}має бути швидшим, оскільки очищення цілого можна залишити в Garbage Collector на потім.
ViFI

8

Як ілюстрацію до вже згаданих речей:

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

Це показує, що .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

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()

Якщо хтось придумає більш практичний приклад, сміливо редагуйте цю публікацію.


global c_kwargsмабуть, теж працюватиме ні? Хоча, мабуть, globalце не найкраще, чим можна користуватися.
фантастичний

3
При використанні @fantabolous globalця функція поводиться по-різному - усі виклики conf_decorator будуть спільно використовувати ту саму змінну c_kwargs. Я вважаю, що Python 3 додав nonlocalключове слово для вирішення цієї проблеми, і це спрацювало б.
Ponkadoodle

1

Крім того, іноді екземпляр dict може бути підкласом dict ( defaultdictнаприклад). У цьому випадку використання clearє кращим, оскільки нам не потрібно пам’ятати точний тип дикту, а також уникати дублювання коду (з’єднання лінії очищення з лінією ініціалізації).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.