Có một sự khác biệt giữa việc sử dụng một dict nghĩa đen và một nhà xây dựng dict?


204

Sử dụng PyCharm, tôi nhận thấy nó cung cấp để chuyển đổi một nghĩa đen :

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

vào một hàm tạo :

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

Những cách tiếp cận khác nhau này khác nhau theo một cách quan trọng?

(Trong khi viết câu hỏi này, tôi nhận thấy rằng việc sử dụng dict()dường như không thể chỉ định khóa số .. d = {1: 'one', 2: 'two'}là có thể, nhưng, rõ ràng dict(1='one' ...)là không. Còn gì nữa không?)


4
dict()lấy danh sách các cặp khóa-giá trị cũng như cho phép các tham số được đặt tên, do đó, nó có thể được sử dụng để tạo bất kỳ loại chính tả nào, không phải với cú pháp bạn đang sử dụng. Có lẽ cũng chẳng có gì đáng ngại khi có lỗi ( youtrack.jetbrains.net/su/PY-2512 ) trong pyCharm đặc biệt vì những gì bạn đã phát hiện ra, đã được sửa).
Wooble

1
có liên quan: stackoverflow.com/questions/5790860/ (tóm tắt: Hành vi của PyCharm chậm hơn và xấu hơn)
Wooble

1
Rõ ràng CPython 2.7 dict () chậm hơn (chậm hơn 6 lần?). Xem: doughellmann.com/2012/11/ Từ Trong mọi trường hợp, tôi bắt đầu thích cú pháp của hàm tạo hơn vì tôi thấy việc nhập và di chuyển mã giữa các lệnh và hàm gọi dễ dàng hơn.
David Wheaton

2
Đừng quên khoảng trắng: bạn không thể tạo khóa chứa khoảng trắng bằng cách thứ hai. Mặc dù vậy, cách đầu tiên có thể lấy bất kỳ chuỗi nào, nó sẽ không quan tâm. Điều tương tự cũng áp dụng cho Unicode, tất nhiên.
CamilB

2
Trong Python 2, hàm dict(abc = 123)tạo tạo một từ điển với các khóa chuỗi byte 'abc', điều này có thể gây ngạc nhiên nếu bạn đang sử dụng unicode_literalsvà mong đợi các khóa từ điển là unicode u'abc'. Xem stackoverflow.com/questions/20357210/ cấp .
Li-aung Yip

Câu trả lời:


116

Tôi nghĩ rằng bạn đã chỉ ra sự khác biệt rõ ràng nhất. Ngoài ra,

đầu tiên không cần phải tra cứu dictmà sẽ làm cho nó nhanh hơn một chút

cái nhìn thứ hai lên dicttrong locals()và sau đó globals()và phát hiện các dựng sẵn, vì vậy bạn có thể chuyển đổi hành vi bằng cách xác định một địa phương gọi là dictví dụ mặc dù tôi không thể nghĩ ra bất cứ nơi nào đây sẽ là một ý tưởng tốt ngoài có lẽ khi gỡ lỗi


4
Một ví dụ về nơi mà một địa phương được gọi là dict có thể hữu ích: stackoverflow.com/a/7880276/313113
cắn

Tôi tin rằng cũng sử dụng dict () trước tiên sẽ xây dựng một dict cho các đối số thành dict () và sau đó sẽ tạo ra một dict thứ hai cho thể hiện dict thực tế được tạo. Niềng răng tạo ra trường hợp dict trong một bước.
NeilG

56

Nghĩa đen nhanh hơn nhiều, vì nó sử dụng các opcode BUILD_MAP và STORE_MAP được tối ưu hóa thay vì CALL_FUNCTION chung:

> 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: Hầu hết thời gian đối với hầu hết người dùng, điều đó không thành vấn đề, nhưng có những tình huống mà hàng triệu hoặc hàng tỷ trong số này được tạo ra và tăng tốc gấp 2 lần là có ý nghĩa.
Ông Fooz

5
@MrFooz: có những tình huống như thế. Tôi nghĩ bạn sẽ thấy rằng 99,9% những người thực hiện vi thời gian không ở trong những tình huống đó.
Ned Batchelder

29
@Ned Nó thích hợp trong một chủ đề hỏi cái nào nhanh hơn.
Elliott

11
@Elliot OP không hỏi cái nào nhanh hơn.
Tom Ferguson

5
Nếu bạn đang tạo ra hàng triệu ký tự hoặc một lệnh với hàng triệu khóa, từ các chữ viết chính tả trong nguồn của bạn, bạn đã làm sai.
jwg

41

Chúng trông khá giống nhau trên Python 3.2.

Như gnibbler đã chỉ ra, việc đầu tiên không cần tra cứu dict, điều này sẽ khiến nó nhanh hơn một chút.

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

Lưu ý rằng trong một số triển khai, đây không thực sự là một "bit nhỏ", giống như hệ số 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

Hai cách tiếp cận này tạo ra các từ điển giống hệt nhau, ngoại trừ, như bạn đã lưu ý, trong đó các quy tắc từ vựng của Python can thiệp.

Từ điển nghĩa đen là một từ điển rõ ràng hơn một chút, và bạn có thể tạo bất kỳ loại khóa nào, nhưng bạn cần trích dẫn tên chính. Mặt khác, bạn có thể sử dụng các biến cho các khóa nếu bạn cần vì một số lý do:

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

Hàm dict()tạo cho phép bạn linh hoạt hơn vì có nhiều dạng đầu vào khác nhau. Ví dụ, bạn có thể cung cấp cho nó một vòng lặp các cặp và nó sẽ coi chúng là các cặp khóa / giá trị.

Tôi không biết tại sao PyCharm lại đề nghị chuyển đổi một dạng này sang dạng khác.


2
Chà, tôi đoán PyCharm chỉ đang cố gắng trở nên tốt đẹp hơn. Giống như nó dường như luôn luôn đề nghị chuyển đổi các chuỗi trích dẫn đơn thành trích dẫn kép - không có lý do rõ ràng.
maligree

1
Bạn chỉ cần trích dẫn khóa của bạn nếu khóa của bạn là chuỗi. Họ có thể dễ dàng trở thành những bộ phao của hàng chục chiếc phao, mặc dù điều này có thể hơi xấu xí.
Wooble

7

Một điểm khác biệt lớn với python 3,4 + pycharm là hàm tạo dict () tạo ra thông báo "lỗi cú pháp" nếu số lượng khóa vượt quá 256.

Tôi thích sử dụng dict nghĩa đen bây giờ.


3
Nó không chỉ là con trăn 3.4. Điều này là do CPython <3.7 có số lượng tối đa 255 đối số theo nghĩa đen được truyền cho một cuộc gọi. ( stackoverflow.com/a/8932175/2718295 )
cowbert

6

Từ hướng dẫn python 2.7:

Một cặp dấu ngoặc nhọn tạo ra một từ điển trống: {}. Đặt một danh sách các cặp khóa: value được phân tách bằng dấu phẩy sẽ thêm các cặp khóa: value ban đầu vào từ điển; đây cũng là cách viết từ điển trên đầu ra.

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

Trong khi:

Hàm tạo dict () xây dựng từ điển trực tiếp từ danh sách các cặp khóa-giá trị được lưu dưới dạng bộ dữ liệu. Khi các cặp tạo thành một mẫu, việc hiểu danh sách có thể chỉ định gọn gàng danh sách khóa-giá trị.

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

Khi các khóa là các chuỗi đơn giản, đôi khi việc chỉ định các cặp sử dụng đối số từ khóa sẽ dễ dàng hơn:

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

Vì vậy, cả {} và dict () đều tạo từ điển nhưng cung cấp một số cách khác nhau để khởi tạo dữ liệu từ điển.


3

Tôi thấy dict nghĩa đen d = {'one': '1'}dễ đọc hơn nhiều, dữ liệu xác định của bạn, thay vì gán các giá trị thứ và gửi chúng cho hàm dict()tạo.

Mặt khác, tôi đã thấy mọi người gõ nhầm chữ theo nghĩa đen d = {'one', '1'}mà trong python hiện đại 2.7+ sẽ tạo ra một bộ.

Mặc dù vậy, tôi vẫn thích sử dụng tất cả các cách sử dụng theo nghĩa đen bởi vì tôi nghĩ rằng sở thích cá nhân dễ đọc hơn của nó.


Tôi thường xuyên quên rằng cú pháp theo nghĩa đen cho set s tồn tại. Tôi ước có một cú pháp theo nghĩa đen cho các lệnh được đặt hàng ... khá chắc chắn rằng tôi sử dụng chúng thường xuyên hơn các bộ.
ArtOfWarfare

2

chữ dict () là tốt khi bạn sao chép các giá trị dán từ một thứ khác (không có python) Ví dụ: một danh sách các biến môi trường. nếu bạn có một tập tin bash, hãy nói

FOO='bar'
CABBAGE='good'

bạn có thể dễ dàng dán sau đó vào một dict()nghĩa đen và thêm ý kiến. Nó cũng làm cho nó dễ dàng hơn để làm ngược lại, sao chép vào một cái gì đó khác. Trong khi đó {'FOO': 'bar'}cú pháp khá độc đáo đối với python và json. Vì vậy, nếu bạn sử dụng json rất nhiều, bạn có thể muốn sử dụng nghĩa {}đen với dấu ngoặc kép.


2

Không có nghĩa đen để tạo ra các lớp kế thừa, các lớp chính tả tùy chỉnh với các phương thức bổ sung. Trong trường hợp như vậy, nên sử dụng hàm tạo lớp dict tùy chỉnh, ví dụ:

class NestedDict(dict):

    # ... skipped

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

0

Ngoài ra, hãy xem xét thực tế rằng các mã thông báo phù hợp với các toán tử không thể được sử dụng trong cú pháp của hàm tạo, tức là các khóa được khử.

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

>>> {'foo-bar': 1}
{'foo-bar': 1}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.