Apakah ada perbedaan antara menggunakan dict literal dan konstruktor dict?

204

Menggunakan PyCharm, saya perhatikan itu menawarkan untuk mengkonversi literal dict :

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

menjadi konstruktor dict :

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

Apakah pendekatan yang berbeda ini berbeda secara signifikan?

(Saat menulis pertanyaan ini saya perhatikan bahwa menggunakannya dict()tampaknya tidak mungkin untuk menentukan kunci numerik .. d = {1: 'one', 2: 'two'}adalah mungkin, tetapi, jelas, dict(1='one' ...)tidak. Ada yang lain?)

maligree
sumber
4
dict()mengambil daftar pasangan nilai kunci serta memungkinkan parameter bernama, sehingga dapat digunakan untuk membuat semua jenis dict, hanya saja tidak dengan sintaks yang Anda gunakan. Mungkin juga tidak ada artinya sama sekali jika ada bug ( youtrack.jetbrains.net/issue/PY-2512 ) di pyCharm secara khusus karena apa yang Anda temukan, yang telah diperbaiki).
Wooble
1
terkait: stackoverflow.com/questions/5790860/… (ringkasan: Perilaku PyCharm lebih lambat dan lebih buruk)
Wooble
1
Rupanya CPython 2.7 dict () lebih lambat (6 kali lebih lambat?). Lihat: doughellmann.com/2012/11/... Bagaimanapun saya mulai lebih suka sintaks konstruktor karena saya merasa lebih mudah untuk mengetik dan memindahkan kode antara dikts dan panggilan fungsi.
David Wheaton
2
Jangan lupa spasi: Anda tidak bisa membuat kunci yang berisi spasi menggunakan cara kedua. Namun, cara pertama dapat mengambil string apa pun, tidak akan peduli. Hal yang sama berlaku untuk Unicode, tentu saja.
CamilB
2
Dalam Python 2, dict(abc = 123)konstruktor menghasilkan kamus dengan kunci byte-string 'abc', yang mungkin mengejutkan jika Anda menggunakan unicode_literalsdan mengharapkan kunci kamus menjadi unicode u'abc'. Lihat stackoverflow.com/questions/20357210/… .
Li-aung Yip

Jawaban:

116

Saya pikir Anda telah menunjukkan perbedaan yang paling jelas. Selain itu,

yang pertama tidak perlu pencarian dictyang seharusnya membuatnya sedikit lebih cepat

terlihat kedua sampai dictdi locals()kemudian globals()dan penemuan builtin, sehingga Anda dapat beralih perilaku dengan mendefinisikan lokal yang disebut dictmisalnya meskipun saya tidak bisa memikirkan di mana ini akan menjadi ide yang baik selain mungkin ketika debugging

John La Rooy
sumber
4
Contoh di mana dict lokal yang disebut mungkin berguna: stackoverflow.com/a/7880276/313113
bitek
Saya percaya juga menggunakan dict () pertama-tama akan membuat dict untuk argumen ke dict () dan kemudian akan membuat dict kedua untuk instance dict aktual yang akan dibuat. Kawat gigi membuat instance dikt dalam satu langkah.
NeilG
56

Literal jauh lebih cepat, karena menggunakan opcode BUILD_MAP dan STORE_MAP yang dioptimalkan daripada CALL_FUNCTION umum:

> 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
sumber
10
@Ned: Sebagian besar waktu bagi sebagian besar pengguna itu tidak masalah sama sekali, tetapi ada situasi di mana jutaan atau miliaran dari ini sedang dibuat dan peningkatan kecepatan 2x bermakna.
Tuan Fooz
5
@ MrFooz: ada situasi seperti itu. Saya pikir Anda akan menemukan bahwa 99,9% orang yang melakukan mikro-timing tidak dalam situasi seperti itu.
Ned Batchelder
29
@Ned Ini terkait dalam utas yang menanyakan yang mana lebih cepat.
Elliott
11
@ Elliot OP tidak menanyakan yang mana yang lebih cepat.
Tom Ferguson
5
Jika Anda menghasilkan jutaan dicts atau satu dict dengan jutaan kunci, dari dict literal di sumber Anda, Anda salah melakukannya.
jwg
41

Mereka terlihat hampir sama di Python 3.2.

Seperti yang ditunjukkan oleh gnibbler, yang pertama tidak perlu pencarian dict, yang seharusnya membuatnya sedikit lebih cepat.

>>> 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
sumber
Perhatikan bahwa dalam beberapa implementasi, ini tidak benar-benar "sedikit", lebih seperti faktor 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

Kedua pendekatan ini menghasilkan kamus yang identik, kecuali, seperti yang telah Anda catat, di mana aturan leksikal Python mengganggu.

Kamus kamus sedikit lebih jelas kamus, dan Anda dapat membuat segala jenis kunci, tetapi Anda perlu mengutip nama-nama kunci. Di sisi lain, Anda dapat menggunakan variabel untuk kunci jika Anda perlu untuk beberapa alasan:

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

The dict()konstruktor memberi Anda lebih banyak fleksibilitas karena berbagai bentuk masukan yang dibutuhkan. Misalnya, Anda bisa memberikan iterator berpasangan, dan itu akan memperlakukan mereka sebagai pasangan kunci / nilai.

Saya tidak tahu mengapa PyCharm akan menawarkan untuk mengkonversi satu bentuk ke bentuk lainnya.

Ned Batchelder
sumber
2
Yah, kurasa PyCharm hanya berusaha menjadi lebih baik. Seperti yang tampaknya selalu menawarkan untuk mengubah string kutipan tunggal menjadi dikutip ganda - tanpa alasan yang jelas.
maligree
1
Anda hanya perlu mengutip kunci Anda jika kunci Anda adalah string. Mereka bisa dengan mudah menjadi tuple frozenset mengapung, meskipun ini mungkin akan sedikit jelek.
Wooble
7

Satu perbedaan besar dengan python 3.4 + pycharm adalah konstruktor dict () menghasilkan pesan "syntax error" jika jumlah kunci melebihi 256.

Saya lebih suka menggunakan dict literal sekarang.

Michel Boiron
sumber
3
Ini bukan hanya python 3.4. Ini karena CPython <3,7 memiliki jumlah maksimum 255 argumen literal yang diteruskan ke callable. ( stackoverflow.com/a/8932175/2718295 )
cowbert
6

Dari tutorial python 2.7:

Sepasang kawat gigi membuat kamus kosong: {}. Menempatkan daftar kunci yang dipisah koma: pasangan nilai dalam kurung menambah kunci awal: pasangan nilai ke kamus; ini juga cara kamus ditulis pada output.

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

Sementara:

Dict () konstruktor membangun kamus langsung dari daftar pasangan nilai kunci yang disimpan sebagai tupel. Ketika pasangan membentuk suatu pola, pemahaman daftar dapat secara kompak menentukan daftar nilai kunci.

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

Ketika kunci adalah string sederhana, terkadang lebih mudah untuk menentukan pasangan menggunakan argumen kata kunci:

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

Jadi {} dan dict () menghasilkan kamus tetapi menyediakan cara inisialisasi data kamus yang sedikit berbeda.

Artsiom Rudzenka
sumber
3

Saya menemukan dict literal d = {'one': '1'}menjadi lebih mudah dibaca, mendefinisikan data Anda, daripada menetapkan nilai hal-hal dan mengirimkannya ke dict()konstruktor.

Di sisi lain saya telah melihat orang salah ketik dict literal d = {'one', '1'}yang dalam python modern 2.7+ akan membuat satu set.

Meskipun demikian saya masih lebih suka untuk semua cara menggunakan himpunan literal karena saya pikir itu lebih mudah dibaca, preferensi pribadi saya kira.

lee penkman
sumber
Saya secara teratur lupa bahwa sintaks literal untuk sets ada. Saya berharap ada sintaks literal untuk perintah dicts ... cukup yakin saya menggunakannya lebih sering daripada set.
ArtOfWarfare
2

dict () literal bagus ketika Anda menyalin nilai paste dari sesuatu yang lain (tidak ada python) Sebagai contoh daftar variabel lingkungan. jika Anda memiliki file bash, katakan

FOO='bar'
CABBAGE='good'

Anda dapat dengan mudah menempelkan ke dict()literal dan menambahkan komentar. Ini juga membuatnya lebih mudah untuk melakukan yang sebaliknya, menyalin ke sesuatu yang lain. Padahal {'FOO': 'bar'}sintaksisnya cukup unik untuk python dan json. Jadi jika Anda banyak menggunakan json, Anda mungkin ingin menggunakan {}literal dengan tanda kutip ganda.

Nick Humrich
sumber
2

Tidak ada dict literal untuk membuat kelas dict diwarisi, kelas dict khusus dengan metode tambahan. Dalam kasus seperti itu konstruktor kelas dikt kustom harus digunakan, misalnya:

class NestedDict(dict):

    # ... skipped

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

Juga pertimbangkan fakta bahwa token yang cocok dengan operator tidak dapat digunakan dalam sintaks konstruktor, yaitu kunci dasherized.

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

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