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

Marcin
źródło
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ą.

Greg Hewgill
źródło
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.

Michel
źródło
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
odano
źródło
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
maxp
źródło
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
Lastland
źródło
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.

Karoly Horvath
źródło
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.

Ponkadoodle
źródło
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)
Tzach
źródło