Czy jest jakaś różnica między używaniem literału dict a konstruktorem dict?

204

Za pomocą PyCharm zauważyłem, że oferuje konwersję dosłownego słownika :

d = {
    'one': '1',
    'two': '2',
}

w konstruktora nagrań :

d = dict(one='1', two='2')

Czy te różne podejścia różnią się w jakiś znaczący sposób?

(Pisząc to pytanie zauważyłem, że użycie dict()klucza numerycznego wydaje się niemożliwe ... d = {1: 'one', 2: 'two'}jest możliwe, ale oczywiście dict(1='one' ...)nie jest. Coś jeszcze?)

maligree
źródło
4
dict()pobiera listę par klucz-wartość, a także zezwala na nazwane parametry, dzięki czemu można go używać do tworzenia dowolnego typu dykt, ale nie ze składnią, której używasz. Prawdopodobnie nie jest również nic warte, że w pyCharm wystąpił błąd ( youtrack.jetbrains.net/issue/PY-2512 ), szczególnie z powodu tego, co odkryłeś, co zostało naprawione).
Wooble,
1
powiązane: stackoverflow.com/questions/5790860/… (streszczenie: zachowanie PyCharm jest wolniejsze i brzydsze)
Wooble,
1
Najwyraźniej CPython 2.7 dict () działa wolniej (6 razy wolniej?). Zobacz: doughellmann.com/2012/11/... W każdym razie zaczynam preferować składnię konstruktora, ponieważ łatwiej mi pisać i przenosić kod między dyktando a wywołaniami funkcji.
David Wheaton,
2
Nie zapominaj o spacjach: nie możesz tworzyć kluczy zawierających spacje za pomocą drugiego sposobu. Pierwszy sposób może jednak przyjąć dowolny ciąg znaków, nie będzie to miało znaczenia. To samo dotyczy oczywiście Unicode.
CamilB
2
W Pythonie 2 dict(abc = 123)konstruktor tworzy słownik z kluczami bajt-ciąg 'abc', co może być zaskakujące, jeśli używasz unicode_literalsi spodziewasz się, że klucze słownika będą Unicode u'abc'. Zobacz stackoverflow.com/questions/20357210/... .
Li-aung Yip

Odpowiedzi:

116

Myślę, że wskazałeś najbardziej oczywistą różnicę. Oprócz tego,

pierwszy nie musi wyszukiwać, dictco powinno nieco przyspieszyć

drugi patrzy dictw locals()czym globals()i odnajduje wbudowane, więc można przełączyć zachowań poprzez zdefiniowanie lokalnego o nazwie dictna przykład, choć nie mogę myśleć o gdziekolwiek to będzie dobry pomysł, oprócz być może podczas debugowania

John La Rooy
źródło
4
Przykład, w którym lokalny o nazwie dict może być przydatny: stackoverflow.com/a/7880276/313113
bitek
Wierzę, że również użycie dict () najpierw konstruuje dykt dla argumentów dict (), a następnie tworzy drugi dykt dla faktycznej instancji, która ma zostać utworzona. Nawiasy klamrowe tworzą instancję dict w jednym kroku.
NeilG
56

Dosłowność jest znacznie szybsza, ponieważ wykorzystuje zoptymalizowane kody BUILD_MAP i STORE_MAP zamiast ogólnych CALL_FUNCTION:

> python2.7 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.958 usec per loop

> python2.7 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.479 usec per loop

> python3.2 -m timeit "d = dict(a=1, b=2, c=3, d=4, e=5)"
1000000 loops, best of 3: 0.975 usec per loop

> python3.2 -m timeit "d = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}"
1000000 loops, best of 3: 0.409 usec per loop
Daniel Kluev
źródło
10
@Ned: W większości przypadków dla większości użytkowników nie ma to żadnego znaczenia, ale są sytuacje, w których tworzone są miliony lub miliardy z nich, a 2x przyspieszenie jest znaczące.
Pan Fooz,
5
@MrFooz: zdarzają się takie sytuacje. Myślę, że okaże się, że 99,9% osób wykonujących mikrozmienianie nie znajduje się w takich sytuacjach.
Ned Batchelder,
29
@Ned Jest to istotne w wątku pytającym, który jest szybszy.
Elliott,
11
@Elliot OP nie zapytał, który jest szybszy.
Tom Ferguson
5
Jeśli produkujesz miliony dyktand lub jeden dykta z milionami kluczy, z literałów dykt w twoim źródle, robisz to źle.
jwg
41

Wyglądają prawie tak samo w Pythonie 3.2.

Jak wskazał gnibbler, pierwszy nie musi wyszukiwać dict, co powinno nieco przyspieszyć.

>>> def literal():
...   d = {'one': 1, 'two': 2}
...
>>> def constructor():
...   d = dict(one='1', two='2')
...
>>> import dis
>>> dis.dis(literal)
  2           0 BUILD_MAP                2
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 ('one')
              9 STORE_MAP
             10 LOAD_CONST               3 (2)
             13 LOAD_CONST               4 ('two')
             16 STORE_MAP
             17 STORE_FAST               0 (d)
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
>>> dis.dis(constructor)
  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_CONST               1 ('one')
              6 LOAD_CONST               2 ('1')
              9 LOAD_CONST               3 ('two')
             12 LOAD_CONST               4 ('2')
             15 CALL_FUNCTION          512
             18 STORE_FAST               0 (d)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE
Paolo Moretti
źródło
Zauważ, że w niektórych implementacjach nie jest to tak naprawdę „ $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' "{'a': 1, 'b': 2, 'c': 3}" ....... Mean +- std dev: 1.73 ns +- 0.14 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' '{k:v for k,v in i}' ....... Mean +- std dev: 139 ns +- 10 ns $ pypy -m perf timeit -l '1000000' -n '5' -s 'i=(("a",1), ("b", 2), ("c", 3))' 'dict(i)' ....... Mean +- std dev: 188 ns +- 16 ns
drobiazg
13

Te dwa podejścia dają identyczne słowniki, z wyjątkiem, jak zauważyłeś, gdzie zakłócają się reguły leksykalne Pythona.

Dosłowne słowniki są nieco bardziej oczywistymi słownikami i możesz stworzyć dowolny klucz, ale musisz podać nazwy kluczy. Z drugiej strony możesz użyć zmiennych dla kluczy, jeśli z jakiegoś powodu potrzebujesz:

a = "hello"
d = {
    a: 'hi'
    }

dict()Konstruktor daje większą elastyczność ze względu na różnorodność form wejścia przez nią działań. Na przykład możesz podać iterator par, który będzie traktował je jako pary klucz / wartość.

Nie mam pojęcia, dlaczego PyCharm zaoferowałby konwersję jednej formy na drugą.

Ned Batchelder
źródło
2
Wydaje mi się, że PyCharm stara się być wyjątkowo miły. Tak jak zawsze wydaje się oferować konwersję ciągów pojedynczych cudzysłowów na cudzysłowy - bez wyraźnego powodu.
maligree
1
Musisz podać swoje klucze tylko wtedy, gdy są to ciągi znaków. Mogą równie dobrze być krotkami zamrożonych pływaków, choć może to być nieco brzydkie.
Wooble,
7

Jedną dużą różnicą w przypadku Pythona 3.4 + pycharm jest to, że konstruktor dict () generuje komunikat „błąd składni”, jeśli liczba kluczy przekracza 256.

Wolę teraz używać literału dict.

Michel Boiron
źródło
3
To nie tylko python 3.4. Jest tak, ponieważ CPython <3.7 ma maksymalną liczbę 255 literalnych argumentów przekazanych do wywołania. ( stackoverflow.com/a/8932175/2718295 )
cowbert
6

Z samouczka Python 2.7:

Para nawiasów tworzy pusty słownik: {}. Umieszczenie rozdzielonej przecinkami listy par klucz: wartość w nawiasach klamrowych dodaje początkowe pary klucz: wartość do słownika; w ten sposób słowniki są zapisywane na wyjściu.

tel = {'jack': 4098, 'sape': 4139}
data = {k:v for k,v in zip(xrange(10), xrange(10,20))}

Podczas:

Konstruktor dict () buduje słowniki bezpośrednio z list par klucz-wartość przechowywanych jako krotki. Gdy pary tworzą wzór, zestawienia list mogą w zwięzły sposób określać listę klucz-wartość.

tel = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) {'sape': 4139, 'jack': 4098, 'guido': 4127}
data = dict((k,v) for k,v in zip(xrange(10), xrange(10,20)))

Gdy klucze są prostymi ciągami, czasem łatwiej jest określić pary za pomocą argumentów słów kluczowych:

dict(sape=4139, guido=4127, jack=4098)
>>>  {'sape': 4139, 'jack':4098, 'guido': 4127}

Zatem zarówno {}, jak i dict () tworzą słownik, ale zapewniają nieco inne sposoby inicjowania danych słownikowych.

Artsiom Rudzenka
źródło
3

Uważam, że dosłowny słownik d = {'one': '1'}jest znacznie bardziej czytelny, ponieważ definiujesz dane, zamiast przypisywać wartości i wysyłać je do dict()konstruktora.

Z drugiej strony widziałem, jak ludzie błędnie wpisują dosłowny słownik, ponieważ d = {'one', '1'}we współczesnym Pythonie 2.7+ stworzy zestaw.

Mimo to nadal wolę na wszelkie sposoby korzystać z zestawu literału, ponieważ myślę, że jest to bardziej czytelna, osobista preferencja.

Lee Penkman
źródło
Regularnie zapominam, że setistnieje dosłowna składnia dla s. Chciałbym, żeby istniała dosłowna składnia dla uporządkowanych dykt ... całkiem pewna, że ​​używam ich częściej niż zbiorów.
ArtOfWarfare
2

literał dict () jest przydatny, gdy kopiujesz wklejanie wartości z czegoś innego (brak pytona) Na przykład z listy zmiennych środowiskowych. powiedzmy, jeśli masz plik bash

FOO='bar'
CABBAGE='good'

możesz łatwo wkleić następnie do dict()literału i dodać komentarze. Ułatwia także odwrotne kopiowanie do czegoś innego. Podczas gdy {'FOO': 'bar'}składnia jest dość unikalna dla Pythona i Jsona. Więc jeśli często używasz Jsona, możesz chcieć używać {}literałów z podwójnymi cudzysłowami.

Nick Humrich
źródło
2

Nie ma literału-dyktu do tworzenia klas dziedziczonych przez dyk, niestandardowych klas-dykt z dodatkowymi metodami. W takim przypadku należy użyć niestandardowego konstruktora klasy dict, na przykład:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})
Dmitrij Sintsov
źródło
0

Weź również pod uwagę fakt, że tokenów pasujących do operatorów nie można używać w składni konstruktora, tj. Klucze przerywane.

>>> dict(foo-bar=1)
File "<stdin>", line 1
SyntaxError: keyword can't be an expression

>>> {'foo-bar': 1}
{'foo-bar': 1}
Brian Whitton
źródło