Tại sao Python không giữ trật tự chèn?


12

Gần đây tôi đã rất ngạc nhiên khi phát hiện ra rằng trong khi các lệnh được đảm bảo duy trì thứ tự chèn trong Python 3.7+, thì các bộ không được:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

Lý do cho sự khác biệt này là gì? Có phải các cải tiến hiệu quả tương tự đã khiến nhóm Python thay đổi việc triển khai dict không áp dụng cho các tập hợp không?

Tôi không tìm kiếm các con trỏ cho các triển khai được đặt theo thứ tự hoặc các cách để sử dụng các ký tự làm điểm nhấn cho các bộ. Tôi chỉ tự hỏi tại sao nhóm Python không tạo ra các bộ dựng sẵn bảo vệ trật tự đồng thời họ đã làm như vậy cho các thiết bị.


1
Điều này có trả lời câu hỏi của bạn không? Python có một bộ được đặt hàng không?
Mihai Chelaru

1
Không, tôi hiểu rằng Python không có bộ cài đặt sẵn. Tôi chỉ thắc mắc tại sao lại như vậy, vì các dicts hiện đã được đặt hàng.
Bart Robinson

4
Các kiểu sử dụng là khác nhau, vì vậy chúng được tối ưu hóa cho các trường hợp sử dụng khác nhau. Đó là một quan niệm sai lầm phổ biến rằng các tập hợp chỉ là các giá trị null trong CPython, điều đó hoàn toàn không chính xác: việc triển khai là khác nhau. Nếu câu hỏi của bạn không được đóng lại, tôi có thể đăng câu trả lời chi tiết.
Wim

1
"Các mô hình sử dụng là khác nhau, vì vậy chúng được tối ưu hóa cho các trường hợp sử dụng khác nhau." Một câu trả lời tốt cho câu hỏi sẽ giải thích về điều này, tôi nghĩ. Câu hỏi là về những gì làm cho hai cách tiếp cận khác nhau tối ưu cho các trường hợp sử dụng tương ứng.
Karl Knechtel

Lưu ý rằng PyPy không sử dụng cùng một thứ tự cho cả hai dictsetkể từ 2.7.
MisterMiyagi

Câu trả lời:


10

Bộ và dicts được tối ưu hóa cho các trường hợp sử dụng khác nhau. Việc sử dụng chính của một bộ là thử nghiệm thành viên nhanh, đó là thuyết bất khả tri. Đối với các thiết bị, chi phí tra cứu là hoạt động quan trọng nhất và chìa khóa có nhiều khả năng có mặt. Với các tập hợp, sự hiện diện hoặc vắng mặt của một phần tử không được biết trước và do đó, việc thực hiện tập hợp cần tối ưu hóa cho cả trường hợp tìm thấy và không tìm thấy. Ngoài ra, một số tối ưu hóa cho các hoạt động tập hợp phổ biến như liên kết và giao cắt làm cho việc giữ lại trật tự thiết lập mà không làm giảm hiệu suất.

Mặc dù cả hai cấu trúc dữ liệu đều dựa trên hàm băm, nhưng đó là một quan niệm sai lầm phổ biến rằng các tập hợp chỉ được triển khai dưới dạng các giá trị null. Ngay cả trước khi triển khai chính tả nhỏ gọn trong CPython 3.6, các triển khai tập hợp và chính tả đã khác nhau đáng kể, với việc sử dụng lại ít mã. Ví dụ, các dicts sử dụng thăm dò ngẫu nhiên, nhưng các bộ sử dụng kết hợp giữa thăm dò tuyến tính và địa chỉ mở, để cải thiện cục bộ bộ đệm. Đầu dò tuyến tính ban đầu ( 9 bước mặc định trong CPython) sẽ kiểm tra một loạt các cặp khóa / băm liền kề, cải thiện hiệu suất bằng cách giảm chi phí xử lý va chạm băm - truy cập bộ nhớ liên tiếp rẻ hơn so với các đầu dò phân tán.

Nó sẽ là tốt về mặt lý thuyết để thay đổi thực hiện bộ CPython cho bạn trở thành tương tự như dict nhỏ gọn, nhưng trong thực tế có những nhược điểm, và các nhà phát triển cốt lõi đáng chú ý được chứ không phải thực hiện một sự thay đổi đó.

Bộ vẫn không có thứ tự. (Tại sao? Các mô hình sử dụng là khác nhau. Ngoài ra, việc thực hiện khác nhau.)

- Guido van Rossum

Các bộ sử dụng một thuật toán khác không thể sửa đổi để giữ lại thứ tự chèn. Các thao tác cài đặt sẽ mất tính linh hoạt và tối ưu hóa nếu yêu cầu đặt hàng. Tập toán học được định nghĩa theo các tập hợp không có thứ tự. Nói tóm lại, đặt hàng không phải là trong tương lai trước mắt.

- Raymond Hettinger

Một cuộc thảo luận chi tiết về việc có nên thu gọn các bộ cho 3.7 hay không và câu trả lời về lý do tại sao nó được quyết định chống lại, có thể được tìm thấy trong danh sách gửi thư của python-dev.

Tóm lại, những điểm chính là các kiểu sử dụng khác nhau (các ký tự đặt hàng chèn như ** kwargs là hữu ích , ít hơn cho các bộ), tiết kiệm không gian cho các bộ nén là ít quan trọng hơn (vì chỉ có mảng khóa và hàm băm tăng cường, trái ngược với các khóa, băm và giá trị) và tối ưu hóa thăm dò tuyến tính đã nói ở trên không tương thích với việc triển khai nhỏ gọn.

Tôi sẽ sao chép bài của Raymond dưới đây bao gồm những điểm quan trọng nhất.

Vào ngày 14 tháng 9 năm 2016, lúc 3:50 PM, Eric Snow đã viết:

Sau đó, tôi sẽ làm tương tự với các bộ.

Trừ khi tôi hiểu lầm, Raymond phản đối việc thực hiện một thay đổi tương tự để thiết lập.

Đúng rồi. Dưới đây là một vài suy nghĩ về chủ đề này trước khi mọi người bắt đầu chạy hoang dã.

  • Đối với lệnh nhỏ gọn, tiết kiệm không gian là một chiến thắng ròng với không gian bổ sung được sử dụng bởi các chỉ số và tổng thể cho các mảng khóa / giá trị / hàm băm được bù đắp nhiều hơn bởi mật độ cải thiện của mảng khóa / giá trị / hàm băm. Tuy nhiên, đối với các bộ, mạng ít thuận lợi hơn nhiều vì chúng ta vẫn cần các chỉ số và tổng thể nhưng chỉ có thể bù chi phí không gian bằng cách chỉ tăng hai trong ba mảng. Nói cách khác, nén chặt có ý nghĩa hơn khi bạn đã lãng phí không gian cho các khóa, giá trị và giá trị băm. Nếu bạn mất một trong ba điều đó, nó sẽ không còn hấp dẫn nữa.

  • Mẫu sử dụng cho các bộ khác với dicts. Các cựu có nhiều lượt truy cập hoặc bỏ lỡ. Cái sau có xu hướng thiếu ít tra cứu quan trọng hơn. Ngoài ra, một số tối ưu hóa cho các hoạt động set-to-set gây khó khăn cho việc duy trì thứ tự thiết lập mà không ảnh hưởng đến hiệu suất.

  • Tôi theo đuổi con đường thay thế để cải thiện hiệu suất thiết lập. Thay vì nén (không chiếm nhiều không gian và phát sinh chi phí cho một lần bổ sung), tôi đã thêm tính năng thăm dò tuyến tính để giảm chi phí va chạm và cải thiện hiệu suất bộ đệm. Cải tiến này không tương thích với phương pháp nén mà tôi ủng hộ cho từ điển.

  • Hiện tại, tác dụng phụ của thứ tự đối với từ điển là không được bảo đảm, do đó, còn sớm để bắt đầu khẳng định các bộ cũng được đặt hàng. Các tài liệu đã liên kết đến một công thức để tạo một Orderedset ( https://code.activestate.com/recipes/576694/ ) nhưng có vẻ như sự hấp thụ đã gần như bằng không. Ngoài ra, bây giờ Eric Snow đã cho chúng tôi một OrderedDict nhanh chóng, việc xây dựng một Orderedset từ Mutableset và OrderedDict trở nên dễ dàng hơn bao giờ hết, nhưng một lần nữa tôi đã không quan sát thấy bất kỳ mối quan tâm thực sự nào vì các phân tích dữ liệu được thiết lập điển hình không thực sự cần hoặc quan tâm đến việc đặt hàng. Tương tự như vậy, việc sử dụng chính của các bài kiểm tra thành viên nhanh là thuyết bất khả tri.

  • Điều đó nói rằng, tôi nghĩ có chỗ để thêm các triển khai tập hợp thay thế cho PyPI. Đặc biệt, có một số trường hợp đặc biệt thú vị đối với dữ liệu có thể sắp xếp trong đó các thao tác cài đặt có thể được tăng tốc bằng cách so sánh toàn bộ phạm vi khóa (xem https://code.activestate.com/recipes/230113-im THỰCation-of- bộ-sử dụng-sắp xếp-danh sách cho một điểm bắt đầu). IIRC, PyPI đã có mã cho các bộ lọc nở giống như set và băm cuckoo.

  • Tôi hiểu rằng thật thú vị khi có một khối mã chính được chấp nhận vào lõi Python nhưng điều đó không mở ra cho lũ lụt để tham gia vào việc viết lại nhiều hơn các kiểu dữ liệu khác trừ khi chúng tôi chắc chắn rằng nó được bảo hành.

- Raymond Hettinger

Từ [Python-Dev] Python 3.6 dict trở nên nhỏ gọn và có phiên bản riêng; và từ khóa được đặt hàng , tháng 9 năm 2016.


2

Thảo luận

Câu hỏi của bạn là vô bổ và đã được thảo luận rất nhiều về python-dev cách đây không lâu. R. Hettinger đã chia sẻ một danh sách các lý do trong chủ đề đó . Tình trạng của vấn đề có vẻ như đã kết thúc mở ngay bây giờ, ngay sau câu trả lời chi tiết này từ T. Peters.

Nói tóm lại, việc thực hiện các dicts hiện đại bảo tồn thứ tự chèn là duy nhất và không được coi là phù hợp với các bộ. Cụ thể, các ký tự được sử dụng ở mọi nơi để chạy Python (ví dụ: __dict__trong không gian tên của các đối tượng). Một động lực chính đằng sau chính tả hiện đại là giảm kích thước, làm cho Python có hiệu quả tổng thể cao hơn về bộ nhớ. Ngược lại, các bộ ít phổ biến hơn các bộ trong lõi của Python và do đó không cho phép tái cấu trúc như vậy. Xem thêm bài nói chuyện của R. Hettinger về việc thực hiện chính tả hiện đại.


Quan điểm

Bản chất không có thứ tự của các bộ trong Python tương đồng với hành vi của các bộ toán học . Đặt hàng không được đảm bảo.

Khái niệm toán học tương ứng không có thứ tự và sẽ thật kỳ lạ khi áp đặt như trật tự - R. Hettinger

Nếu thứ tự của bất kỳ loại nào được đưa vào các tập hợp trong Python, thì hành vi này sẽ tuân thủ một cấu trúc toán học hoàn toàn riêng biệt, cụ thể là một tập hợp có thứ tự (hoặc Oset). Osets chơi một cuộn riêng trong toán học, đặc biệt là trong tổ hợp. Một ứng dụng thực tế của Osets được quan sát thấy trong việc thay đổi chuông .

Có các tập hợp không có thứ tự phù hợp với cấu trúc dữ liệu rất chung chung và phổ biến, giúp giải quyết hầu hết toán học hiện đại, tức là Lý thuyết tập hợp . Tôi gửi, các bộ không có thứ tự trong Python là tốt để có.

Xem thêm bài viết liên quan mở rộng về chủ đề này:

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.