มีความแตกต่างระหว่างการใช้ตัวอักษร dict และตัวสร้าง dict ไหม?


204

เมื่อใช้ PyCharm ฉันสังเกตว่ามันเสนอให้แปลงตัวอักษรตามคำบอก :

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

เป็นตัวสร้าง dict :

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

วิธีการที่แตกต่างกันเหล่านี้แตกต่างกันไปในวิธีที่สำคัญหรือไม่?

(ในขณะที่เขียนคำถามนี้ฉันสังเกตเห็นว่าการใช้dict()ดูเหมือนว่าเป็นไปไม่ได้ที่จะระบุคีย์ตัวเลข .. d = {1: 'one', 2: 'two'}เป็นไปได้ แต่เห็นได้ชัดว่าdict(1='one' ...)ไม่ใช่สิ่งอื่นใด)


4
dict()รับรายการคู่คีย์ - ค่าเช่นเดียวกับการอนุญาตให้พารามิเตอร์ที่มีชื่อดังนั้นมันสามารถใช้เพื่อสร้าง dict ชนิดใดก็ได้โดยไม่ต้องใช้ไวยากรณ์ที่คุณใช้ นอกจากนี้ยังอาจไม่มีค่าใด ๆ ที่มีข้อผิดพลาด ( youtrack.jetbrains.net/issue/PY-2512 ) ใน pyCharm โดยเฉพาะเนื่องจากสิ่งที่คุณค้นพบซึ่งได้รับการแก้ไขแล้ว)
Wooble

1
ที่เกี่ยวข้อง: stackoverflow.com/questions/5790860/… (สรุป: พฤติกรรมของ PyCharm ช้าลงและไม่อัปลักษณ์)
Wooble

1
เห็นได้ชัดว่า CPython 2.7 dict () ช้าลง (ช้าลง 6 เท่า?) ดู: doughellmann.com/2012/11/…ไม่ว่าในกรณีใดฉันเริ่มชอบสร้างตัวสร้างไวยากรณ์ต่อไปเนื่องจากฉันพบว่าพิมพ์และย้ายรหัสระหว่าง dicts และการเรียกฟังก์ชันได้ง่ายขึ้น
David Wheaton

2
อย่าลืมช่องว่าง: คุณไม่สามารถสร้างกุญแจที่มีช่องว่างโดยใช้วิธีที่สอง แม้ว่าวิธีแรกสามารถรับสายได้ แต่ก็ไม่สนใจ เช่นเดียวกับ Unicode แน่นอน
CamilB

2
ในหลาม 2 dict(abc = 123)ตัวสร้างผลิตพจนานุกรมด้วยปุ่มไบต์สตริง'abc'ซึ่งอาจจะเป็นที่น่าแปลกใจถ้าคุณกำลังใช้unicode_literalsและคาดหวังคีย์พจนานุกรมจะเป็น u'abc'Unicode ดูstackoverflow.com/questions/20357210/...
Li-aung Yip

คำตอบ:


116

ฉันคิดว่าคุณได้ชี้ให้เห็นความแตกต่างที่ชัดเจนที่สุด นอกเหนือจากนี้,

ครั้งแรกไม่จำเป็นต้องค้นหาdictซึ่งควรทำให้เร็วขึ้นเล็กน้อย

ลักษณะที่สองขึ้นdictในlocals()แล้วglobals()และที่พบในตัวเพื่อให้คุณสามารถเปลี่ยนพฤติกรรมโดยการกำหนดท้องถิ่นที่เรียกว่าdictยกตัวอย่างเช่นแม้ว่าฉันไม่สามารถคิดได้ทุกที่นี้จะเป็นความคิดที่ดีอาจจะแตกต่างจากเมื่อแก้จุดบกพร่อง


4
ตัวอย่างตำแหน่งที่ dict ในพื้นที่อาจมีประโยชน์: stackoverflow.com/a/7880276/313113
bitek

ฉันเชื่อว่าการใช้ dict () จะสร้าง dict ก่อนเพื่อให้อาร์กิวเมนต์เป็น dict () และจากนั้นจะสร้าง dict ที่สองสำหรับอินสแตนซ์ dict จริงที่จะสร้าง เครื่องมือจัดฟันสร้างอินสแตนซ์ dict ในขั้นตอนเดียว
NeilG

56

ตัวอักษรเร็วกว่ามากเนื่องจากใช้ตัวเลือก BUILD_MAP และตัวเลือก STORE_MAP ที่ได้รับการปรับปรุงให้ดีกว่า 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

10
@Ned: ส่วนใหญ่เวลาสำหรับผู้ใช้ส่วนใหญ่มันไม่สำคัญเลย แต่มีสถานการณ์ที่มีคนนับล้านหรือหลายพันล้านคนกำลังสร้างและการเร่งความเร็ว 2x นั้นมีความหมาย
Mr Fooz

5
@MrFooz: มีสถานการณ์แบบนั้น ฉันคิดว่าคุณจะพบว่า 99.9% ของคนที่ทำเวลาไมโครไม่ได้อยู่ในสถานการณ์เหล่านั้น
Ned Batchelder

29
@ เน็ดมันเกี่ยวข้องกับหัวข้อที่ถามว่าอันไหนเร็วกว่า
เอลเลียต

11
@Elliot OP ไม่ได้ถามว่าอันไหนเร็วกว่า
Tom Ferguson

5
หากคุณกำลังผลิต dicts นับล้านหรือ dict หนึ่งอันพร้อมปุ่มนับล้านจาก dict ตัวอักษรในแหล่งที่มาของคุณแสดงว่าคุณทำผิด
jwg

41

พวกมันดูคล้ายกันมากกับ Python 3.2

ตามที่ gnibbler ชี้ให้เห็นคนแรกไม่จำเป็นต้องค้นหาdictซึ่งควรทำให้เร็วขึ้นเล็กน้อย

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

โปรดทราบว่าในการติดตั้งใช้งานบางอย่างนี่ไม่ได้เป็น "นิด ๆ หน่อย ๆ " เหมือนกับปัจจัย 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

วิธีการทั้งสองนี้สร้างพจนานุกรมที่เหมือนกันยกเว้นตามที่คุณได้บันทึกไว้โดยที่กฎศัพท์ของ Python รบกวน

ตัวอักษรในพจนานุกรมเป็นพจนานุกรมที่ชัดเจนกว่านี้เล็กน้อยและคุณสามารถสร้างคีย์ได้ทุกชนิด แต่คุณต้องอ้างชื่อคีย์ ในทางกลับกันคุณสามารถใช้ตัวแปรสำหรับคีย์ได้หากคุณต้องการด้วยเหตุผลบางประการ:

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

คอนdict()สตรัคให้ความยืดหยุ่นมากขึ้นเนื่องจากมีรูปแบบการป้อนข้อมูลที่หลากหลาย ตัวอย่างเช่นคุณสามารถจัดให้มีตัววนซ้ำของคู่และมันจะถือว่าพวกเขาเป็นคู่คีย์ / ค่า

ฉันไม่รู้ว่าทำไม PyCharm จะเสนอให้แปลงฟอร์มหนึ่งเป็นอีกฟอร์มหนึ่ง


2
ฉันเดาว่า PyCharm แค่พยายามเป็นคนดีมาก ๆ ดูเหมือนว่าจะมีการเสนอให้แปลงสตริงที่มีการเสนอราคาเดี่ยวเป็นการเสนอราคาสองครั้งเสมอโดยไม่มีเหตุผลที่ชัดเจน
maligree

1
คุณจะต้องพูดคีย์ของคุณถ้าคีย์ของคุณเป็นสตริง พวกมันสามารถเป็นได้ง่าย ๆ เป็นอันดับหนึ่งของโฟลเซ็ตของโฟลทแม้ว่ามันอาจจะดูน่าเกลียดก็ตาม
Wooble

7

ข้อแตกต่างที่สำคัญอย่างหนึ่งของ python 3.4 + pycharm คือตัวสร้าง dict () สร้างข้อความ "ข้อผิดพลาดทางไวยากรณ์" หากจำนวนของคีย์เกิน 256

ฉันชอบที่จะใช้ตัวอักษร Dict ตอนนี้


3
มันไม่ใช่แค่งูหลาม 3.4 นี่เป็นเพราะ CPython <3.7 มีอาร์กิวเมนต์ที่แท้จริงจำนวน 255 ตัวที่ส่งผ่านไปยัง callable ( stackoverflow.com/a/8932175/2718295 )
cowbert

6

จากการกวดวิชาหลาม 2.7:

เครื่องมือจัดฟันคู่สร้างพจนานุกรมว่างเปล่า: {} การวางรายการคีย์ที่คั่นด้วยเครื่องหมายจุลภาค: คู่ของค่าภายในวงเล็บปีกกาเพิ่มคีย์เริ่มต้น: คู่ค่าให้กับพจนานุกรม; นี่เป็นวิธีที่พจนานุกรมเขียนบนเอาต์พุต

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

ในขณะที่:

ตัวสร้าง dict () สร้างพจนานุกรมโดยตรงจากรายการของคู่ของคีย์ - ค่าที่เก็บไว้เป็นสิ่งอันดับ เมื่อทั้งคู่เกิดรูปแบบรายการความเข้าใจในรายการสามารถระบุรายการคีย์ - ค่าได้อย่างกระชับ

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

เมื่อคีย์เป็นสตริงอย่างง่ายบางครั้งก็ง่ายกว่าในการระบุคู่โดยใช้อาร์กิวเมนต์คำหลัก:

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

ดังนั้นทั้ง {} และ dict () จึงสร้างพจนานุกรม แต่ให้วิธีการเริ่มต้นข้อมูลพจนานุกรมที่แตกต่างกันเล็กน้อย


3

ฉันพบว่าตัวอักษร dict d = {'one': '1'}นั้นสามารถอ่านได้มากขึ้นข้อมูลที่กำหนดของคุณแทนที่จะกำหนดค่าต่าง ๆ และส่งไปยังตัวdict()สร้าง

ในทางกลับกันฉันได้เห็นคนพิมพ์ผิดตัวอักษร Dict d = {'one', '1'}ซึ่งใน python สมัยใหม่ 2.7+ จะสร้างชุด

อย่างไรก็ตามเรื่องนี้ฉันยังคงชอบทุกวิธีใช้ชุดตัวอักษรเพราะฉันคิดว่ามันอ่านง่ายขึ้นการตั้งค่าส่วนตัวฉันคิดว่า


ฉันมักจะลืมว่ามีไวยากรณ์ที่แท้จริงสำหรับsets ฉันหวังว่าจะมีไวยากรณ์ที่แท้จริงสำหรับ dicts ที่สั่ง ... ค่อนข้างแน่ใจว่าฉันใช้พวกเขาบ่อยกว่าชุด
ArtOfWarfare

2

ตัวอักษร dict () ดีเมื่อคุณคัดลอกค่าการวางจากสิ่งอื่น (ไม่มีไพ ธ อน) ตัวอย่างเช่นรายการของตัวแปรสภาพแวดล้อม ถ้าคุณมีไฟล์ bash ให้พูด

FOO='bar'
CABBAGE='good'

คุณสามารถวางลงในdict()ตัวอักษรและเพิ่มความคิดเห็นได้อย่างง่ายดาย นอกจากนี้ยังช่วยให้ทำสิ่งที่ตรงกันข้ามได้ง่ายขึ้นคัดลอกไปยังสิ่งอื่น ในขณะที่{'FOO': 'bar'}ไวยากรณ์นั้นค่อนข้างเป็นเอกลักษณ์ของ python และ json ดังนั้นหากคุณใช้ json {}บ่อยครั้งคุณอาจต้องการใช้ตัวอักษรที่มีเครื่องหมายคำพูดคู่


2

ไม่มี dict ตามตัวอักษรเพื่อสร้างคลาส dict ที่สืบทอดคลาส dict ที่กำหนดเองด้วยวิธีการเพิ่มเติม ในกรณีเช่นนั้นตัวสร้างคลาส Dict แบบกำหนดเองควรใช้ตัวอย่างเช่น:

class NestedDict(dict):

    # ... skipped

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

0

พิจารณาข้อเท็จจริงที่ว่าโทเค็นที่ตรงกับตัวดำเนินการไม่สามารถใช้ในไวยากรณ์ของตัวสร้างเช่นคีย์การแดชคีย์

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

>>> {'foo-bar': 1}
{'foo-bar': 1}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.