Gibt es einen Unterschied zwischen der Verwendung eines Dikt-Literal und eines Dikt-Konstruktors?

204

Bei der Verwendung von PyCharm ist mir aufgefallen, dass es die Konvertierung eines Dikt-Literals bietet :

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

in einen Diktatkonstruktor :

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

Unterscheiden sich diese unterschiedlichen Ansätze in signifikanter Weise?

(Während ich diese Frage schrieb, bemerkte ich, dass dict()es unmöglich erscheint, einen numerischen Schlüssel anzugeben. Dies d = {1: 'one', 2: 'two'}ist möglich, aber offensichtlich dict(1='one' ...)nicht. Sonst noch etwas?)

bösartig
quelle
4
dict()Nimmt eine Liste von Schlüssel-Wert-Paaren und lässt benannte Parameter zu, sodass damit jede Art von Diktat erstellt werden kann, nur nicht mit der von Ihnen verwendeten Syntax. Es ist wahrscheinlich auch nichts wert, dass in pyCharm ein Fehler ( youtrack.jetbrains.net/issue/PY-2512 ) aufgetreten ist, insbesondere aufgrund dessen, was Sie entdeckt haben und der behoben wurde.
Wooble
1
verwandt: stackoverflow.com/questions/5790860/… (Zusammenfassung: PyCharms Verhalten ist langsamer und hässlicher)
Wooble
1
Anscheinend ist CPython 2.7 dict () langsamer (6 mal langsamer?). Siehe: teigellmann.com/2012/11/… Auf jeden Fall bevorzuge ich sowieso die Konstruktorsyntax, da ich es einfacher finde, Code zwischen Diktaten und Funktionsaufrufen einzugeben und zu verschieben.
David Wheaton
2
Leerzeichen nicht vergessen: Auf die zweite Weise können Sie keine Schlüssel erstellen, die Leerzeichen enthalten. Der erste Weg kann jedoch jede Zeichenfolge aufnehmen, es ist ihm egal. Gleiches gilt natürlich auch für Unicode.
CamilB
2
In Python 2 erstellt der dict(abc = 123)Konstruktor ein Wörterbuch mit Byte-String-Schlüsseln. Dies 'abc'kann überraschend sein, wenn Sie unicode_literalsWörterbuchschlüssel verwenden und erwarten, dass sie Unicode sind u'abc'. Siehe stackoverflow.com/questions/20357210/… .
Li-aung Yip

Antworten:

116

Ich denke, Sie haben auf den offensichtlichsten Unterschied hingewiesen. Abgesehen davon,

Das erste muss nicht nachgeschlagen werden, dictwas es ein bisschen schneller machen sollte

Der zweite schaut dictnach locals()und dann globals()und findet den eingebauten, so dass Sie das Verhalten ändern können, indem Sie beispielsweise einen lokalen Namen definieren, dictobwohl ich mir nirgendwo vorstellen kann, dass dies eine gute Idee wäre, außer vielleicht beim Debuggen

John La Rooy
quelle
4
Ein Beispiel dafür, wo ein lokales Diktat
Bitek
Ich glaube, dass auch die Verwendung von dict () zuerst ein Diktat für die Argumente für dict () erstellt und dann ein zweites Diktat für die tatsächlich zu erstellende Diktatinstanz erstellt. Klammern erstellen die Diktatinstanz in einem Schritt.
NeilG
56

Literal ist viel schneller, da es optimierte BUILD_MAP- und STORE_MAP-Opcodes anstelle der generischen CALL_FUNCTION verwendet:

> 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
quelle
10
@Ned: Meistens spielt es für die meisten Benutzer keine Rolle, aber es gibt Situationen, in denen Millionen oder Milliarden davon erstellt werden und eine zweifache Beschleunigung sinnvoll ist.
Herr Fooz
5
@ MrFooz: Es gibt solche Situationen. Ich denke, Sie werden feststellen, dass 99,9% der Leute, die Mikro-Timings durchführen, nicht in solchen Situationen sind.
Ned Batchelder
29
@Ned Es ist relevant in einem Thread zu fragen, welcher schneller ist.
Elliott
11
@Elliot Das OP hat nicht gefragt, welches schneller ist.
Tom Ferguson
5
Wenn Sie entweder Millionen von Diktaten oder ein Diktat mit Millionen von Schlüsseln aus Diktatliteralen in Ihrer Quelle erstellen, machen Sie es falsch.
JWG
41

Sie sehen in Python 3.2 ziemlich gleich aus.

Wie Gnibbler betonte, muss der erste nicht nachschlagen dict, was es ein bisschen schneller machen sollte.

>>> 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
quelle
Beachten Sie, dass dies in einigen Implementierungen kein "kleines bisschen" ist, sondern eher ein Faktor von 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
DylanYoung
13

Diese beiden Ansätze erzeugen identische Wörterbücher, außer wie Sie bemerkt haben, wo die lexikalischen Regeln von Python interferieren.

Wörterbuchliterale sind etwas offensichtlicher Wörterbücher, und Sie können jede Art von Schlüssel erstellen, aber Sie müssen die Schlüsselnamen zitieren. Auf der anderen Seite können Sie Variablen für Schlüssel verwenden, wenn Sie dies aus irgendeinem Grund benötigen:

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

Der dict()Konstruktor bietet Ihnen mehr Flexibilität aufgrund der Vielzahl der Eingabeformen. Sie können ihm beispielsweise einen Iterator von Paaren bereitstellen, der sie als Schlüssel / Wert-Paare behandelt.

Ich habe keine Ahnung, warum PyCharm anbieten würde, ein Formular in das andere zu konvertieren.

Ned Batchelder
quelle
2
Nun, ich denke PyCharm versucht nur, besonders nett zu sein. So wie es immer zu bieten scheint, Zeichenfolgen in einfachen Anführungszeichen in doppelte Anführungszeichen umzuwandeln - ohne ersichtlichen Grund.
Maligree
1
Sie müssen Ihre Schlüssel nur zitieren, wenn Ihre Schlüssel Zeichenfolgen sind. Sie könnten genauso gut Tupel von Frozensets von Schwimmern sein, obwohl dies ein bisschen hässlich werden könnte.
Wooble
7

Ein großer Unterschied zu Python 3.4 + Pycharm besteht darin, dass der Konstruktor dict () eine Meldung "Syntaxfehler" ausgibt, wenn die Anzahl der Schlüssel 256 überschreitet.

Ich bevorzuge es jetzt, das Diktal zu verwenden.

Michel Boiron
quelle
3
Es ist nicht nur Python 3.4. Dies liegt daran, dass in CPython <3.7 maximal 255 Literalargumente an einen Aufrufer übergeben werden. ( stackoverflow.com/a/8932175/2718295 )
Cowbert
6

Aus Python 2.7 Tutorial:

Ein Klammerpaar erstellt ein leeres Wörterbuch: {}. Durch Platzieren einer durch Kommas getrennten Liste von Schlüssel: Wert-Paaren in geschweiften Klammern werden dem Wörterbuch erste Schlüssel: Wert-Paare hinzugefügt. Auf diese Weise werden auch Wörterbücher bei der Ausgabe geschrieben.

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

Während:

Der Konstruktor dict () erstellt Wörterbücher direkt aus Listen von Schlüssel-Wert-Paaren, die als Tupel gespeichert sind. Wenn die Paare ein Muster bilden, kann das Listenverständnis die Schlüsselwertliste kompakt angeben.

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

Wenn die Schlüssel einfache Zeichenfolgen sind, ist es manchmal einfacher, Paare mithilfe von Schlüsselwortargumenten anzugeben:

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

Sowohl {} als auch dict () erzeugen also ein Wörterbuch, bieten jedoch etwas unterschiedliche Möglichkeiten für die Initialisierung von Wörterbuchdaten.

Artsiom Rudzenka
quelle
3

Ich finde, dass das Dikt-Literal d = {'one': '1'}viel besser lesbar ist, Ihre Daten zu definieren, anstatt Dinge Werte zuzuweisen und sie an den dict()Konstruktor zu senden .

Auf der anderen Seite habe ich Leute gesehen, die das Dikt-Literal d = {'one', '1'}falsch geschrieben haben, als ob in modernem Python 2.7+ eine Menge erstellt wird.

Trotzdem bevorzuge ich es immer noch, das Set-Literal auf alle Arten zu verwenden, weil ich denke, dass es lesbarer und persönlicher ist, nehme ich an.

Lee Penkman
quelle
Ich vergesse regelmäßig, dass die wörtliche Syntax für sets existiert. Ich wünschte, es gäbe eine wörtliche Syntax für geordnete Diktate ... ziemlich sicher, dass ich sie häufiger als Sätze verwende.
ArtOfWarfare
2

Das dictal-Literal ist hilfreich, wenn Sie Einfügewerte von etwas anderem kopieren (keine Python). Zum Beispiel eine Liste von Umgebungsvariablen. Wenn Sie eine Bash-Datei hatten, sagen Sie

FOO='bar'
CABBAGE='good'

Sie können dann einfach in ein dict()Literal einfügen und Kommentare hinzufügen. Es macht es auch einfacher, das Gegenteil zu tun und in etwas anderes zu kopieren. Während die {'FOO': 'bar'}Syntax für Python und JSON ziemlich einzigartig ist. Wenn Sie also häufig json verwenden, möchten Sie möglicherweise {}Literale mit doppelten Anführungszeichen verwenden.

Nick Humrich
quelle
2

Es gibt kein Dikt-Literal zum Erstellen von von Dikt geerbten Klassen, benutzerdefinierte Dikt-Klassen mit zusätzlichen Methoden. In diesem Fall sollte ein benutzerdefinierter Konstruktor für die Diktatklasse verwendet werden, zum Beispiel:

class NestedDict(dict):

    # ... skipped

state_type_map = NestedDict(**{
    'owns': 'Another',
    'uses': 'Another',
})
Dmitriy Sintsov
quelle
0

Berücksichtigen Sie auch die Tatsache, dass Token, die mit Operatoren übereinstimmen, nicht in der Konstruktorsyntax verwendet werden können, dh dasherisierte Schlüssel.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}
Brian Whitton
quelle