Există o diferență între utilizarea unui dict literal și a unui constructor dict? (Programare, Python, Dicționar, Pycharm)

maligree a intrebat.

Folosind PyCharm, am observat că oferă posibilitatea de a converti un dict literal:

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

într-un dict constructor:

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

Diferă aceste abordări diferite într-un mod semnificativ?

(În timp ce scriam această întrebare am observat că utilizarea dict() pare imposibil să se specifice o cheie numerică … d = {1: 'one', 2: 'two'} este posibilă, dar, evident, , dict(1='one' ...) nu este. Altceva?)

Comentarii

  • dict() acceptă o listă de perechi cheie-valoare, precum și permite parametrii numiți, astfel încât poate fi folosit pentru a crea orice tip de dict, doar că nu cu sintaxa pe care o utilizați. De asemenea, probabil că nu are nicio valoare faptul că a existat un bug (youtrack.jetbrains.net/issue/PY-2512) în pyCharm în special din cauza a ceea ce ați descoperit, care a fost reparat). –  > Por Wooble.
  • related: stackoverflow.com/questions/5790860/… (rezumat: PyCharm’s behavior is slower and uglier) –  > Por Wooble.
  • Se pare că CPython 2.7 dict() este mai lent (de 6 ori mai lent?). Vedeți: doughellmann.com/2012/11/11/… În orice caz, încep să prefer oricum sintaxa constructorului, deoarece mi se pare mai ușor de tastat și de mutat codul între dict și apeluri de funcții. –  > Por David Wheaton.
  • Nu uitați de spații: nu puteți crea chei care conțin spații folosind a doua modalitate. Primul mod, însă, poate accepta orice șir de caractere, nu-i va păsa. Același lucru este valabil și pentru Unicode, bineînțeles. –  > Por CamilB.
  • În Python 2, opțiunea dict(abc = 123) produce un dicționar cu chei de tip șir de octeți 'abc', , ceea ce poate fi surprinzător dacă folosiți unicode_literals și vă așteptați ca cheile dicționarului să fie unicode u'abc'. A se vedea stackoverflow.com/questions/20357210/…. –  > Por Li-aung Yip.
10 răspunsuri
John La Rooy

Cred că ați evidențiat cea mai evidentă diferență. În afară de asta,

primul nu are nevoie să caute dict ceea ce ar trebui să-l facă puțin mai rapid

a doua caută dict în locals() și apoi globals() iar cea din urmă găsește builtinul, astfel încât puteți schimba comportamentul prin definirea unui local numit dict de exemplu, deși nu mă pot gândi la niciun loc unde acest lucru ar fi o idee bună, cu excepția, poate, a situației în care se face depanarea.

Comentarii

  • Un exemplu de situație în care un dict numit local ar putea fi util: stackoverflow.com/a/7880276/313113 –  > Por Alex Bitek.
  • Cred că și utilizarea dict() va construi mai întâi un dict pentru argumentele către dict() și apoi va crea un al doilea dict pentru instanța dict propriu-zisă care urmează să fie creată. Braces creează instanța dict într-un singur pas. –  > Por NeilG.
  • dict ar putea fi combinat cu zip pentru a converti două matrici paralele (adică legate) într-o singură mapare de asociere. –  > Por jpaugh.
Daniel Kluev

Literal este mult mai rapid, deoarece utilizează codurile operaționale optimizate BUILD_MAP și STORE_MAP mai degrabă decât CALL_FUNCTION generic:

> 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

Comentarii

  • @Ned: În cea mai mare parte a timpului, pentru majoritatea utilizatorilor, nu contează deloc, dar există situații în care se creează milioane sau miliarde de astfel de fișiere și o creștere de 2 ori a vitezei este semnificativă. –  > Por Dl Fooz.
  • @MrFooz: există astfel de situații. Cred că veți constata că 99,9% dintre cei care fac microtimpuri nu se află în astfel de situații. –  > Por Ned Batchelder.
  • 30

  • @Ned Este relevant într-un thread în care se întreabă care este mai rapid totuși. –  > Por Elliott.
  • @Elliot OP nu a întrebat care dintre ele este mai rapidă. –  > Por Tom Ferguson.
  • Dacă produceți fie milioane de dicte, fie un dict cu milioane de chei, din dicte literale din sursa dvs., nu procedați corect. –  > Por jwg.
Paolo Moretti

Arată cam la fel în Python 3.2.

După cum a subliniat gnibbler, primul nu are nevoie să caute dict, , ceea ce ar trebui să o facă puțin mai rapidă.

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

Comentarii

  • Rețineți că, în unele implementări, acest lucru nu este chiar un „pic”, ci mai degrabă un factor de 100: $ 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 –  > Por DylanYoung.
Ned Batchelder

Aceste două abordări produc dicționare identice, cu excepția, după cum ați observat, a cazurilor în care intervin regulile lexicale din Python.

Dicționarele literale sunt un pic mai evident dicționare și puteți crea orice tip de cheie, dar trebuie să citați numele cheilor. Pe de altă parte, puteți folosi variabile pentru chei dacă aveți nevoie din anumite motive:

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

La dict() constructorul vă oferă mai multă flexibilitate datorită varietății formelor de intrare pe care le acceptă. De exemplu, puteți să-i furnizați un iterator de perechi, iar acesta le va trata ca perechi cheie/valoare.

Nu am nicio idee de ce PyCharm s-ar oferi să convertească o formă în alta.

Comentarii

  • Ei bine, cred că PyCharm încearcă doar să fie foarte drăguț. La fel cum se pare că întotdeauna se oferă să convertească șirurile între ghilimele simple în șiruri între ghilimele duble – fără niciun motiv aparent. –  > Por maligree.
  • Trebuie să vă citați cheile doar dacă cheile sunt șiruri de caractere. Acestea ar putea fi la fel de ușor tuple de frozensets de float, deși acest lucru ar putea deveni puțin urât. –  > Por Wooble.
Michel Boiron

O mare diferență față de python 3.4 + pycharm este că constructorul dict() produce un mesaj de „eroare de sintaxă” dacă numărul de chei depășește 256.

Prefer să folosesc acum literalul dict.

Comentarii

  • Nu este vorba doar de python 3.4. Acest lucru se datorează faptului că CPython < 3.7 are un număr maxim de 255 de argumente literale transmise unui callable. (stackoverflow.com/a/8932175/2718295) –  > Por cowbert.
Artsiom Rudzenka

Din tutorialul python 2.7:

O pereche de paranteze creează un dicționar gol: {}. Plasarea unei liste de perechi cheie:valoare separate prin virgulă în interiorul parantezelor adaugă perechi cheie:valoare inițiale la dicționar; acesta este, de asemenea, modul în care dicționarele sunt scrise la ieșire.

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

În timp ce:

Constructorul dict() construiește dicționare direct din liste de perechi cheie-valoare stocate ca tușe. Atunci când perechile formează un model, înțelegerile de liste pot specifica în mod compact lista cheie-valoare.

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

Atunci când cheile sunt simple șiruri de caractere, este uneori mai ușor să se specifice perechile folosind argumente de tip cuvânt cheie:

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

Astfel, atât {}, cât și dict() produc dicționare, dar oferă modalități puțin diferite de inițializare a datelor din dicționare.

lee penkman

Consider că literalul dict d = {'one': '1'} să fie mult mai ușor de citit, definirea datelor, mai degrabă decât atribuirea de valori lucrurilor și trimiterea lor către dict() constructor.

Pe de altă parte, am văzut oameni care au scris greșit literalul dict ca fiind d = {'one', '1'} care în Python 2.7+ modern va crea un set.

În ciuda acestui fapt, eu prefer să folosesc întotdeauna literalul set, deoarece cred că este mai ușor de citit, preferințe personale, presupun.

Comentarii

  • În mod regulat uit că sintaxa literală pentru sets există. Mi-aș dori să existe o sintaxă literală pentru dicte ordonate… sunt destul de sigur că le folosesc mai des decât seturile. –  > Por ArtOfWarfare.
Nick Humrich

literalul dict() este frumos atunci când copiați și lipiți valori din altceva (nu din python)De exemplu, o listă de variabile de mediu.dacă ați avea un fișier bash, să zicem

FOO='bar'
CABBAGE='good'

puteți lipi cu ușurință acel fișier într-un fișier dict() literal și să adăugați comentarii. De asemenea, face mai ușor să faci opusul, să copiezi în altceva. În timp ce {'FOO': 'bar'} sintaxa este destul de unică pentru python și json. Așadar, dacă folosiți mult json, ar fi bine să folosiți {} literale cu ghilimele duble.

Dmitriy Sintsov

Nu există un literal dict pentru a crea clase moștenite de dict, clase dict personalizate cu metode suplimentare. În acest caz, ar trebui să se utilizeze constructorul de clase dict personalizate, de exemplu:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})

Brian Whitton

De asemenea, luați în considerare faptul că token-urile care se potrivesc pentru operatori nu pot fi utilizate în sintaxa constructorului, adică cheile dasherizate.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}