2012-04-18 21 views
5

Tôi có một dict như:tại sao toán tử 'in' với tuple là khóa trong python quá chậm?

d=dict() 
d[('1','2')] = 'value' 

Sau đó, tôi truy vấn chính:

if (k1,k2) in d.keys(): 

Khi có triệu bản, tốc độ là một sự đau khổ, bất kỳ vấn đề với các 'trong' điều hành?

Có tìm kiếm tuần tự không?

Tôi phải đặt đường nối là chìa khóa để bỏ qua vấn đề này.

+0

Bạn có thể cho chúng tôi biết mã bạn đã sử dụng để đi đến kết luận này không? –

+0

Khá thú vị vấn đề, tôi sẽ xem xét điều này –

+0

Chỉnh sửa của bạn giới thiệu một câu hỏi hoàn toàn mới: tại sao bạn không hỏi nó trong một câu hỏi riêng biệt? Tôi nghĩ điều đó sẽ có ý nghĩa hơn. –

Trả lời

12

Bạn nên sử dụng

(k1,k2) in d 

thay vì gọi d.keys().

Thực hiện theo cách của bạn, trong Python 2 sẽ dẫn đến tìm kiếm tuyến tính và thay vào đó phủ nhận lợi ích của số dict. Trong Python 3 mã của bạn là hiệu quả (xem ý kiến ​​dưới đây) nhưng phiên bản của tôi là rõ ràng hơn.

+0

Điều này có đúng với cả Python 2 và 3 không? Tôi nghĩ '.keys()' sẽ cung cấp một đối tượng xem trong Python 3, không phải là một danh sách? –

+3

Tôi đang đấu tranh để xem tại sao ai đó thậm chí sẽ sử dụng '' d.keys() ''. Trong Python 2.x, điều này sẽ tạo ra một danh sách một triệu bản ghi mỗi lần. Ngay cả trong 3.x, nó vẫn hoàn toàn dư thừa. –

+0

@TimPietzcker Ngay cả khi nó tạo ra một đối tượng xem có thể lặp lại, toán tử 'in' sẽ phải thực hiện tìm kiếm tuyến tính. Bạn cần áp dụng toán tử 'in' vào đối tượng' dict'. –

5

Ngoài việc bổ sung cho Nolen Royalty, tôi nghĩ tôi sẽ lưu ý rằng bạn thực sự có thể thực hiện các bài kiểm tra timeit theo cách tốt hơn một chút. Bằng cách di chuyển việc xây dựng các dict vào một chức năng thiết lập, chúng tôi chỉ có thể thời gian hoạt động trên dict, cho chúng ta một kết quả mà chúng ta có thể so sánh dễ dàng.

Trong 3.2:

python -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d.keys()' '_ = (1, 2) in d.keys()' 
1000000 loops, best of 3: 0.404 usec per loop 

python -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d' '_ = (1, 2) in d' 
1000000 loops, best of 3: 0.247 usec per loop 

Bạn có thể thấy sự khác biệt. Trong 3.x, làm việc trực tiếp trên dict mang đến cho chúng ta tốc độ tăng gần gấp 2 lần, điều này không tệ.

Trong 2.7.3:

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d.keys(); _ = (1, 2) in d.keys()' 
10 loops, best of 3: 36.3 msec per loop 

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d' '_ = (1, 2) in d' 
10000000 loops, best of 3: 0.197 usec per loop 

Trong 2.x, sự khác biệt thực sự là đáng kinh ngạc . Sử dụng dict.keys() mất 36,300 micro giây, trong khi chỉ dict mất dưới 0,2 micro giây. Đó là gần hai trăm nghìn lần nhanh hơn.

Chỉ cần nghĩ rằng đó là đáng lưu ý.

Chỉnh sửa:

Tim đã đưa ra nhận xét thú vị, vì vậy tôi đã quyết định làm thử nghiệm kiểm tra. Tôi đã cố gắng chỉ xây dựng danh sách, và sau đó chỉ cần làm việc tra cứu băm, kết quả như sau:

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' 'd.keys()' 'd.keys()' 
100 loops, best of 3: 5.84 msec per loop 

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' -s 'l=d.keys()' '_ = ("1", "2") in l' '_ = ("1", "2") in l' 
10 loops, best of 3: 25.3 msec per loop 

Bạn có thể thấy rằng trên một dict lớn như thế này, xây dựng danh sách mất khoảng 1/6 thời gian, làm tìm kiếm thông qua danh sách 5/6 của thời gian.

+2

Tuyệt vời - và tôi khá chắc chắn rằng sự khác biệt lớn này là * không * chủ yếu vì tìm kiếm tuyến tính so với tra cứu băm nhưng vì việc xây dựng danh sách các khóa cho mỗi lần lặp lại, như bạn đã đề xuất bình luận của bạn cho câu trả lời của @ DavidHeffernan. –

+0

Cảm ơn rất nhiều về mẹo, tôi không biết bạn có thể dễ dàng vượt qua một chức năng thiết lập như thế. +1 –

+0

@NolenRoyalty Nó thực sự hữu ích - bạn cũng có thể thực hiện nhiều thiết lập bằng cách sử dụng cờ nhiều lần. Bạn cũng có thể tránh sử dụng dấu chấm phẩy bằng cách đặt nhiều chuỗi được trích dẫn hơn. Làm cho các bài kiểm tra phức tạp trở nên dễ dàng hơn nhiều. –

Các vấn đề liên quan