Tại sao từ điển python không thể đảo ngược cho python3.7?


11

Bắt đầu từ 3.7, từ điển python tiêu chuẩn được đảm bảo để duy trì thứ tự chèn. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

Nói cách khác, các phím dict được giữ theo một trật tự nghiêm ngặt. Về nguyên tắc, điều này sẽ cho phép các phím có thể đảo ngược. Tuy nhiên, không có tác phẩm nào sau đây:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Câu hỏi: lý do đằng sau hành vi này là gì? Tại sao các dicts không được thực hiện đảo ngược? Có kế hoạch nào để thay đổi hành vi này trong tương lai không?

Cách giải quyết của khóa học hoạt động:

for k in reversed(list(d.keys())): 
    print(k)

(*) Như một vấn đề thực tế, đây là trường hợp đã được cài đặt điển hình cho python 3.6, như được thảo luận trong bài viết này .


Cập nhật : Bắt đầu với python 3,8 dicts thực sự có thể đảo ngược. Câu trả lời được chấp nhận đề cập đến cuộc thảo luận giữa Guido và các nhà phát triển cốt lõi khác dẫn đến quyết định này. Tóm lại, họ thống nhất về ngôn ngữ đối với các nỗ lực triển khai và lợi ích thực tế cho người dùng.

Câu trả lời:


5

Từ các tài liệu :

đảo ngược ( seq )

Trả lại một ngược lại iterator. seq phải là một đối tượng có __reversed__()phương thức hoặc hỗ trợ giao thức chuỗi ( __len__()phương thức và __getitem__()phương thức có đối số nguyên bắt đầu từ 0).

Một dictđối tượng không thực hiện __reversed__. Nó thực hiện cả hai phương pháp sau. Tuy nhiên, __getitem__lấy các khóa làm đối số, thay vì số nguyên (bắt đầu từ 0).

Về lý do, điều này đã được đề xuất và thảo luận ở đây .

BIÊN TẬP:

Các trích dẫn này được lấy từ danh sách gửi thư của Python-Dev (chủ đề "Thêm phương thức __Vversed__ cho dict", bắt đầu vào ngày 25. 05. 18), tôi sẽ bắt đầu với các đối số "khái niệm", đầu tiên là từ Antoine Pitrou:

Không có gì đáng để OrderedDict hỗ trợ đảo ngược (). Đối số có thể đi cả hai cách:

  1. dict tương tự như OrderedDict ngày nay, vì vậy nó cũng hỗ trợ đảo ngược ();

  2. bạn có thể sử dụng OrderedDict để báo hiệu rõ ràng rằng bạn quan tâm đến việc đặt hàng; không cần thêm bất cứ điều gì để dict.

Tôi nghĩ rằng thứ tự chèn được đảm bảo cho các dicts thông thường là hoàn toàn mới, vì vậy sẽ mất một thời gian để khái niệm ổn định và trở thành một phần của suy nghĩ hàng ngày về các dicts. Một khi điều đó xảy ra, có lẽ không thể tránh khỏi các trường hợp sử dụng sẽ xuất hiện và __Vversed__ sẽ được thêm vào một lúc nào đó. Việc thực hiện có vẻ đơn giản và nó không phải là một bước nhảy vọt về mặt khái niệm để mong đợi rằng một bộ sưu tập có trật tự hữu hạn sẽ có thể đảo ngược.

Tiếp theo là trả lời của Raymond Hettinger:

Cho rằng các dicts hiện theo dõi thứ tự chèn, có vẻ hợp lý khi muốn biết các phần chèn gần đây nhất (nghĩa là lặp qua các tác vụ được thêm gần đây nhất trong một lệnh tác vụ). Các trường hợp sử dụng có thể khác sẽ có khả năng tương ứng với cách chúng ta sử dụng lệnh đuôi Unix.

Nếu những trường hợp sử dụng đó phát sinh, sẽ rất tốt nếu __Vversed__ đã được hỗ trợ để mọi người sẽ không muốn thực hiện một cách giải quyết xấu xí bằng cách sử dụng các cuộc gọi popitem () sau đó là các cuộc gọi lại.

Mối quan tâm chính được thể hiện trong danh sách gửi thư điều này sẽ làm tăng quá nhiều sự phình to hoặc giảm hiệu quả bộ nhớ (phải có danh sách liên kết đôi thay vì danh sách liên kết đơn) trong ít nhất một số triển khai, đây là trích dẫn của Inada Naoki từ trình theo dõi lỗi Python ( số 33462 ):

"Có một đơn đặt hàng" không có nghĩa là "đảo ngược". Ví dụ, danh sách liên kết đơn được sắp xếp, nhưng không thể đảo ngược.

Mặc dù triển khai CPython có thể cung cấp hiệu quả __reverse__, nhưng việc thêm __reverse__có nghĩa là tất cả việc triển khai Python được dự kiến ​​sẽ cung cấp cho nó. Ví dụ, một số triển khai Python có thể thực hiện dict với hashmap + danh sách liên kết đơn. Nếu __reverse__được thêm vào, nó không thể thực hiện được nữa.

Quay lại danh sách gửi thư, đây là hai tin nhắn cuối cùng (cả hai được đăng vào ngày 08/06/2012). Đầu tiên là từ Michael Selik:

Tôi có đúng không khi nói rằng sự đồng thuận là +1 để đưa vào v3.8?

Điểm cuối cùng trong chuỗi là INADA Naoki nghiên cứu các triển khai khác nhau và quyết định rằng có thể đưa tính năng này vào 3.8. Theo tôi hiểu, Guido đã đồng ý với lời khuyên của INADA để chờ đợi triển khai phiên bản 3.7 của MicroPython. Vì INADA đã thay đổi suy nghĩ, tôi đoán tất cả đều có lợi?

Kết luận với thông điệp của Guido van Rossum:

Điều đó nghe có vẻ đúng với tôi. Sau đó chúng tôi sẽ có hai phiên bản trong trường hợp này:

  • 3.6 nơi bảo quản đơn hàng được thực hiện trong CPython nhưng theo thông số ngôn ngữ

  • 3.7 nơi nó cũng được thêm vào thông số ngôn ngữ

Như đã lưu ý trong câu trả lời và nhận xét khác, reversed()được hỗ trợ cho cả bản chính và bản chính tả kể từ phiên bản 3.8 (14.10.2018).


5
Trích dẫn thứ hai của bạn có vẻ như là một lựa chọn thiên vị từ chủ đề đó. Sự đồng thuận dường như là chức năng sẽ được thêm vào 3.8. Ngoài ra trước python 3.7 đơn giản là không có thứ tự nào trên một dictvật thể bình thường (ít nhất là không được bảo đảm từ ngôn ngữ) nên reversedcũng không có ý nghĩa gì
FlyingTeller

Tôi không có một con chó trong cuộc chiến, tôi chỉ trích dẫn phản ứng đầu tiên của nó. Nhưng bạn đã đúng, điểm tốt - tôi đã xóa trích dẫn.
gst

1
Cảm ơn. Chủ đề thảo luận của python-dev đã được tiết lộ. Như một vấn đề thực tế, tính năng này đã được triển khai trong python 3.8, được phát hành chỉ hai ngày trước (14 tháng 10 năm 2019).
Normanius


Các trích dẫn tài liệu là không hữu ích, nó chỉ đặt ra câu hỏi tiếp theo "vì vậy, tại sao loại dict không thực hiện __reversed__"? Liên kết python-dev có nội dung hữu ích, nhưng các phần có liên quan nên được sao chép trực tiếp trong câu trả lời (vì các liên kết ngoài trang web này có xu hướng bị thối).
wim


1

Cập nhật cho python 3.8

Dict và dictview bây giờ có thể lặp lại theo thứ tự chèn đảo ngược bằng cách sử dụng đảo ngược ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>

Câu trả lời này có đóng góp bất cứ điều gì mới chưa được bao phủ bởi các câu trả lời, nhận xét hoặc câu hỏi khác không?
Normanius

@normanius Nó có một mẫu mã hình ảnh lil, có thể hữu ích cho trình duyệt nhanh
jamylak
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.