Różnica między dict.clear () a przypisywaniem {} w Pythonie


167

Czy w Pythonie istnieje różnica między wywołaniem clear()a przypisaniem {}do słownika? Jeśli tak, co to jest? Przykład:

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


Zastanawiam się, czy to robi różnicę w części dotyczącej usuwania śmieci. Czuję, że .clear () powinno być ładniejsze dla systemu pamięci.
Xavier Nicollet

Odpowiedzi:


285

Jeśli masz inną zmienną również odnoszącą się do tego samego słownika, jest duża różnica:

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

Dzieje się tak, ponieważ przypisywanie d = {}tworzy nowy, pusty słownik i przypisuje go do dzmiennej. To pozostawia d2wskazanie na stary słownik, w którym wciąż znajdują się pozycje. Jednak d.clear()czyści ten sam słownik, który di d2oba wskazują.


7
Dzięki. To ma sens. Nadal muszę się przyzwyczaić do sposobu myślenia, który = tworzy odniesienia w Pythonie ...
Marcin

15
= kopiuje odniesienia do nazw. W Pythonie nie ma zmiennych, są tylko obiekty i nazwy.
tzot

17
Chociaż twoje stwierdzenie „bez zmiennych” jest pedantycznie prawdziwe, nie jest tutaj zbyt pomocne. Tak długo, jak dokumentacja języka Python nadal mówi o „zmiennych”, nadal będę używał terminu: docs.python.org/reference/datamodel.html
Greg Hewgill,

9
Komentarz tzota okazał się pomocny w dostosowaniu mojego myślenia o nazwach, zmiennych i typach kopii. Nazywanie tego pedantycznym może być twoją opinią, ale uważam to za niesprawiedliwie surowy wyrok.
cfwschmidt

1
Również clear () nie niszczą usuniętego obiektu w dyktandzie, do którego nadal może się odwoływać ktoś inny.
Lorenzo Belli

31

d = {}utworzy nową instancję dla, dale wszystkie inne odniesienia będą nadal wskazywały na starą zawartość. d.clear()zresetuje zawartość, ale wszystkie odwołania do tej samej instancji będą nadal poprawne.


21

Oprócz różnic wymienionych w innych odpowiedziach istnieje również różnica prędkości. d = {} jest ponad dwukrotnie szybsze:

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
To nie jest poprawny test szybkości dla wszystkich przypadków, ponieważ dyktando jest puste. Myślę, że zrobienie dużego dyktu (lub przynajmniej części treści) przyniosłoby znacznie mniejszą różnicę w wydajności ... a ponadto podejrzewam, że śmieciarz może dodać trochę własnego bólu do d = {} (?)
Rafe

3
@Rafe: Myślę, że chodzi o to, że jeśli wiemy, że żadna inna zmienna nie wskazuje na słownik d, to ustawienie d = {}powinno być szybsze, ponieważ czyszczenie całości można pozostawić w Garbage Collector na później.
ViFI

8

Jako ilustracja rzeczy już wspomnianych:

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

To pokazuje, że .clearmodyfikuje obiekt, ale `= {}` tworzy nowy obiekt.
wizzwizz4

7

Oprócz odpowiedzi @odano wydaje się, że użycie d.clear()jest szybsze, jeśli chcesz wielokrotnie wyczyścić dyktando.

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)

Wynik to:

20.0367929935
19.6444659233

4
Nie jestem pewien, czy różnica jest znacząca. W każdym razie na moim komputerze wyniki są odwrotne!
Aristide

7

Metody mutujące są zawsze przydatne, jeśli oryginalny obiekt nie znajduje się w zakresie:

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

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

Ponowne przypisanie słownika spowodowałoby utworzenie nowego obiektu i nie zmodyfikowałoby oryginalnego.


4

Jedna rzecz, o której nie wspomniano, to kwestie dotyczące zakresu. Niezbyt dobry przykład, ale oto przypadek, w którym napotkałem problem:

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

Rozwiązanie to zastępuje c_kwargs = {}sięc_kwargs.clear()

Jeśli ktoś wymyśli bardziej praktyczny przykład, nie krępuj się edytować tego posta.


global c_kwargsprawdopodobnie też by działało, nie? Chociaż prawdopodobnie globalnie jest najlepszą rzeczą, gdy używa się ich dużo.
fantabolous

3
Użycie @fantabolous globalspowodowałoby, że funkcja zachowywałaby się inaczej - wszystkie wywołania conf_decorator współdzieliłyby wówczas tę samą zmienną c_kwargs. Uważam, że Python 3 dodał nonlocalsłowo kluczowe, aby rozwiązać ten problem, i to zadziała.
Ponkadoodle

1

Ponadto czasami instancja dict może być podklasą dict ( defaultdictna przykład). W takim przypadku clearpreferowane jest użycie , ponieważ nie musimy pamiętać dokładnego typu dyktu, a także unikać duplikowania kodu (łączenia linii rozliczeniowej z linią inicjalizacyjną).

x = defaultdict(list)
x[1].append(2)
...
x.clear() # instead of the longer x = defaultdict(list)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.