2009-03-19 9 views
49

Vì vậy, giả sử tôi muốn tạo từ điển. Chúng tôi sẽ gọi nó là d. Nhưng có nhiều cách để khởi tạo một từ điển bằng Python! Ví dụ, tôi có thể làm điều này:Sự khác nhau giữa dict() và {} là gì?

d = {'hash': 'bang', 'slash': 'dot'} 

Hoặc tôi có thể làm điều này:

d = dict(hash='bang', slash='dot') 

Hoặc này, tò mò:

d = dict({'hash': 'bang', 'slash': 'dot'}) 

Hoặc này:

d = dict([['hash', 'bang'], ['slash', 'dot']]) 

Và toàn bộ vô số cách khác với dict() chức năng. Vì vậy, rõ ràng một trong những điều dict() cung cấp là tính linh hoạt trong cú pháp và khởi tạo. Nhưng đó không phải là những gì tôi hỏi.

Giả sử tôi đã thực hiện d chỉ là một từ điển trống. Điều gì xảy ra ở hậu trường của trình thông dịch Python khi tôi thực hiện d = {} so với d = dict()? Nó chỉ đơn giản là hai cách để làm điều tương tự? Việc sử dụng {} có số cuộc gọi bổ sung của dict() không? Có ai có (thậm chí không đáng kể) chi phí cao hơn người kia? Trong khi câu hỏi thực sự hoàn toàn không quan trọng, đó là một sự tò mò mà tôi rất muốn trả lời.

+0

tôi thấy rõ ràng hơn rất nhiều lời giải thích ở đây: https://doughellmann.com/blog/2012/11/12/the-performance-impact-of -using-dict-instead-of-in-cpython-2-7-2/ – theBuzzyCoder

Trả lời

61
>>> def f(): 
...  return {'a' : 1, 'b' : 2} 
... 
>>> def g(): 
...  return dict(a=1, b=2) 
... 
>>> g() 
{'a': 1, 'b': 2} 
>>> f() 
{'a': 1, 'b': 2} 
>>> import dis 
>>> dis.dis(f) 
    2   0 BUILD_MAP    0 
       3 DUP_TOP    
       4 LOAD_CONST    1 ('a') 
       7 LOAD_CONST    2 (1) 
      10 ROT_THREE   
      11 STORE_SUBSCR   
      12 DUP_TOP    
      13 LOAD_CONST    3 ('b') 
      16 LOAD_CONST    4 (2) 
      19 ROT_THREE   
      20 STORE_SUBSCR   
      21 RETURN_VALUE   
>>> dis.dis(g) 
    2   0 LOAD_GLOBAL    0 (dict) 
       3 LOAD_CONST    1 ('a') 
       6 LOAD_CONST    2 (1) 
       9 LOAD_CONST    3 ('b') 
      12 LOAD_CONST    4 (2) 
      15 CALL_FUNCTION   512 
      18 RETURN_VALUE   

dict() dường như được tích hợp sẵn trong C. Một người thực sự thông minh hoặc tận tụy (không phải tôi) có thể nhìn vào nguồn thông dịch và cho bạn biết thêm. Tôi chỉ muốn thể hiện dis.dis. :)

+1

Thật không may là không ai có thể cho bạn biết những gì đằng sau 'g' vì' CALL_FUNCTION 512' ... –

+2

Hãy xem bài viết hay này của Doug Hellmann: http://doughellmann.com/2012/11/12/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – ezdazuzena

+1

Con trăn mới hơn đã tạo ra ' {'a': 1, 'b': 2} 'hiệu quả hơn. ví dụ. trong python 2.7.10, việc tháo gỡ sẽ thay thế 'ROT_THREE; STORE_SUBSCR; Hướng dẫn DUP_TOP' với 'STORE_MAP'. Trong python 3.5.1 nó loại bỏ tất cả chúng và chỉ có một 'BUILD_MAP'. – danio

31

Theo như hiệu suất đi:

>>> from timeit import timeit 
>>> timeit("a = {'a': 1, 'b': 2}") 
0.424... 
>>> timeit("a = dict(a = 1, b = 2)") 
0.889... 
+0

Tôi nhận được kết quả tương tự cho từ điển trống: 0.1usec cho 'x = {}' so với 0.3usec cho 'x = dict()'. –

+0

Có; các cuộc gọi hàm rất tốn kém. Nhưng hình thức đó linh hoạt hơn, tức là dict (zip (... – DNS

+0

chính xác) các cuộc gọi hàm có giá đắt hơn từ 80-100 lần trong Python, so với C. – vartec

8

Về cơ bản, {} là cú pháp và được xử lý trên một ngôn ngữ và bytecode cấp. dict() chỉ là một nội trang dựng sẵn khác với cú pháp khởi tạo linh hoạt hơn. Lưu ý rằng dict() chỉ được thêm vào giữa chuỗi 2.x.

5

Cập nhật: cảm ơn câu trả lời. Loại bỏ suy đoán về copy-on-write.

Một khác biệt khác giữa {}dictdict luôn phân bổ một cuốn từ điển mới (ngay cả khi nội dung là tĩnh) trong khi {} không luôn làm như vậy (xem mgood's answer cho khi nào và tại sao):

def dict1(): 
    return {'a':'b'} 

def dict2(): 
    return dict(a='b') 

print id(dict1()), id(dict1()) 
print id(dict2()), id(dict2()) 

sản xuất:

 
$ ./mumble.py 
11642752 11642752 
11867168 11867456 

tôi không gợi ý bạn cố gắng tận dụng lợi thế của việc này hay không, nó DEPE nds về tình hình cụ thể, chỉ cần chỉ ra. (Nó cũng có thể hiển nhiên từ disassembly nếu bạn hiểu các opcodes).

+2

Đó không phải là những gì đang xảy ra ở đây. {} vẫn phân bổ một dict mới (nếu không nó sẽ rất xấu), nhưng thực tế là bạn không giữ nó sống có nghĩa là id có thể được tái sử dụng sau khi nó chết (ngay sau khi dict đầu tiên được in). – Brian

+0

Tôi nghi ngờ cuộc gọi chức năng đang hoạt động khác nhau vì nó đang khuấy động không gian id() nhiều hơn một chút để có được một id khác nhau trong cuộc gọi thứ hai. – Brian

+0

Tôi nghĩ câu trả lời của mgood đã làm rõ mọi thứ; Tôi đã cập nhật mục nhập của mình. –

24

@Jacob: Có sự khác biệt về cách phân bổ đối tượng, nhưng chúng không được sao chép. Python phân bổ một "danh sách miễn phí" có kích thước cố định, nơi nó có thể nhanh chóng phân bổ các đối tượng từ điển (cho đến khi nó điền). Từ điển được phân bổ qua cú pháp {} (hoặc gọi C đến PyDict_New) có thể đến từ danh sách miễn phí này.Khi từ điển không còn được tham chiếu, nó sẽ được trả về danh sách miễn phí và khối bộ nhớ đó có thể được sử dụng lại (mặc dù các trường được đặt lại trước).

từ điển đầu tiên này được ngay lập tức quay trở lại danh sách miễn phí, và tiếp theo sẽ sử dụng lại không gian bộ nhớ của nó:

>>> id({}) 
340160 
>>> id({1: 2}) 
340160 

Nếu bạn giữ một tài liệu tham khảo, từ điển tiếp theo sẽ đến từ các khe miễn phí tiếp theo:

>>> x = {} 
>>> id(x) 
340160 
>>> id({}) 
340016 

Nhưng chúng ta có thể xóa các tài liệu tham khảo để mà từ điển và khe cắm của nó miễn phí một lần nữa:

>>> del x 
>>> id({}) 
340160 

Vì cú pháp {} được xử lý bằng mã byte, nó có thể sử dụng tối ưu hóa được đề cập ở trên. Mặt khác, dict() được xử lý như một hàm tạo lớp thông thường và Python sử dụng bộ cấp phát bộ nhớ chung, không theo một mẫu có thể dự đoán dễ dàng như danh sách miễn phí ở trên.

Ngoài ra, hãy xem compile.c từ Python 2.6, với cú pháp {} có vẻ như kích thước trước hashtable dựa trên số lượng mục lưu trữ được biết tại thời gian phân tích cú pháp.

3

dict() được sử dụng khi bạn muốn tạo ra một từ điển từ một iterable, như:

dict(generator which yields (key,value) pairs) 
dict(list of (key,value) pairs) 
+1

Có. Và các trường hợp có thể sử dụng '{…}' thay vào đó nên làm như vậy vì nó trực tiếp và nhanh hơn lời gọi tới hàm tạo 'dict()' (hàm gọi đắt tiền, bằng Python, và tại sao gọi hàm chỉ trả về một cái gì đó có thể được xây dựng trực tiếp thông qua cú pháp '{…}?). – EOL

+0

Tôi tin rằng {...} chỉ hoạt động trong python3 – peufeu

+0

Nó đã hoạt động trong Python 2 từ khá lâu rồi. – EOL

-1

Để tạo một tập rỗng chúng ta nên sử dụng từ khóa đặt trước nó tức set() điều này tạo ra một trống bộ nơi như trong dicts chỉ khung hoa có thể tạo một dict trống

Cho phép đi với một ví dụ

print isinstance({},dict) 
True 
print isinstance({},set) 
False 
print isinstance(set(),set) 
True 
0

sử dụng vui:

def func(**kwargs): 
     for e in kwargs: 
     print(e) 
    a = 'I want to be printed' 
    kwargs={a:True} 
    func(**kwargs) 
    a = 'I dont want to be printed' 
    kwargs=dict(a=True) 
    func(**kwargs) 

đầu ra:

I want to be printed 
a 
+1

Không chắc chắn nếu bạn có nghĩa là funny như trong 'lol' hoặc funny như trong 'lỗi'. Xin vui lòng bao gồm một số loại bình luận hoặc giải thích cho điều này vì câu hỏi này đã được trả lời 8 năm trước, và điều này sẽ để lại noobs đến một mức độ bối rối –