Dict trong Python có duy trì trật tự không?

PEP này đề xuất một từ điển có thứ tự làm cấu trúc dữ liệu mới cho mô-đun collections, được gọi là “OrderedDict” trong PEP này. API được đề xuất kết hợp những trải nghiệm thu được khi làm việc với các triển khai tương tự tồn tại trong các ứng dụng trong thế giới thực khác nhau và các ngôn ngữ lập trình khác

Một Py3 đang hoạt động. 1 bản vá bao gồm các bài kiểm tra và tài liệu có tại

Bản vá Dict đã đặt hàng

Việc đăng ký đã được sửa đổi. 70101 và 70102

Trong các phiên bản Python hiện tại, loại dict tích hợp được sử dụng rộng rãi không chỉ định thứ tự cho các cặp khóa/giá trị được lưu trữ. Điều này gây khó khăn cho việc sử dụng từ điển làm nơi lưu trữ dữ liệu cho một số trường hợp sử dụng cụ thể

Một số ngôn ngữ lập trình động như PHP và Ruby 1. 9 đảm bảo một thứ tự nhất định trên lần lặp. Trong các ngôn ngữ đó và các triển khai chính tả theo thứ tự Python hiện có, thứ tự của các mục được xác định theo thời điểm chèn khóa. Các phím mới được thêm vào cuối, nhưng các phím bị ghi đè sẽ không được di chuyển đến cuối

Ví dụ sau đây cho thấy hành vi đối với các nhiệm vụ đơn giản

>>> d = OrderedDict()
>>> d['parrot'] = 'dead'
>>> d['penguin'] = 'exploded'
>>> d.items()
[('parrot', 'dead'), ('penguin', 'exploded')]

Việc thứ tự được giữ nguyên làm cho OrderedDict trở nên hữu ích trong một số trường hợp

  • Các thư viện xử lý XML/HTML hiện bỏ thứ tự các thuộc tính, sử dụng danh sách thay vì lệnh khiến cho việc lọc trở nên cồng kềnh hoặc triển khai từ điển theo thứ tự của riêng chúng. Điều này ảnh hưởng đến ElementTree, html5lib, Genshi và nhiều thư viện khác
  • Có nhiều triển khai dict theo thứ tự trong các thư viện và ứng dụng khác nhau, hầu hết chúng không tương thích với nhau một cách tinh vi. Hơn nữa, phân lớp dict là một nhiệm vụ không tầm thường và nhiều triển khai không ghi đè đúng tất cả các phương thức, điều này có thể dẫn đến kết quả không mong muốn

    Ngoài ra, nhiều lệnh được đặt hàng được triển khai theo cách không hiệu quả, khiến nhiều hoạt động trở nên phức tạp hơn nên chúng phải được

  • PEP 3115 cho phép siêu dữ liệu thay đổi đối tượng ánh xạ được sử dụng cho nội dung lớp. Một dict có thứ tự có thể được sử dụng để tạo các khai báo thành viên có thứ tự tương tự như cấu trúc C. Điều này có thể hữu ích, ví dụ, đối với các bản phát hành ctypes trong tương lai cũng như các ORM xác định các bảng cơ sở dữ liệu dưới dạng các lớp, giống như lớp mà khung công tác Django cung cấp. Django hiện đang sử dụng một bản hack xấu xí để khôi phục thứ tự của các thành viên trong các mô hình cơ sở dữ liệu
  • Lớp RawConfigParser chấp nhận đối số
    >>> d = OrderedDict([('a', 'b'), ('c', 'd')])
    >>> d.update({'foo': 'bar'})
    >>> d
    collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
    
    0 cho phép ứng dụng đặt loại từ điển được sử dụng nội bộ. Động lực cho sự bổ sung này rõ ràng là để cho phép người dùng cung cấp một từ điển theo thứ tự.
  • Mã được chuyển từ các ngôn ngữ lập trình khác như PHP thường phụ thuộc vào lệnh được sắp xếp. Việc triển khai từ điển bảo toàn thứ tự trong thư viện chuẩn có thể dễ dàng chuyển đổi và cải thiện khả năng tương thích của các thư viện khác nhau

API dict được đặt hàng hầu hết sẽ tương thích với dict và các dict được đặt hàng hiện có. Ghi chú. PEP này đề cập đến 2. 7 và 3. 0 API từ điển như được mô tả trong bộ sưu tập. Ánh xạ lớp cơ sở trừu tượng

Hàm tạo và

>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
1 đều chấp nhận các lần lặp của bộ dữ liệu cũng như ánh xạ giống như một lệnh. Không giống như một từ điển thông thường, thứ tự chèn được giữ nguyên

>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])

Nếu các ký tự có thứ tự được cập nhật từ các ký tự thông thường, tất nhiên thứ tự của các khóa mới là không xác định

Tất cả các phương thức lặp cũng như

>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
2,
>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
3 và
>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
4 trả về các giá trị được sắp xếp theo thời điểm khóa được chèn lần đầu tiên

>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]

Phương pháp mới không có sẵn trên dict

>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
5Hỗ trợ lặp ngược bằng phím

Điều gì xảy ra nếu một khóa hiện có được gán lại?

Khóa không được di chuyển nhưng được gán một giá trị mới tại chỗ. Điều này phù hợp với các triển khai hiện có

Điều gì xảy ra nếu các phím xuất hiện nhiều lần trong danh sách được chuyển đến hàm tạo?

Giống như đối với các ký tự thông thường - mục sau sẽ ghi đè lên mục trước. Điều này có tác dụng phụ là vị trí của khóa đầu tiên được sử dụng vì chỉ giá trị thực sự bị ghi đè

>>> OrderedDict([('a', 1), ('b', 2), ('a', 3)])
collections.OrderedDict([('a', 3), ('b', 2)])

Hành vi này phù hợp với các triển khai hiện có trong Python, mảng PHP và hashmap trong Ruby 1. 9

Lệnh dict có phải là lớp con dict không?

Đúng. Giống như

>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
6, một phân lớp từ điển được sắp xếp theo thứ tự
>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
7. Là một lớp con dict làm cho một số phương thức nhanh hơn (như
>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
8 và
>>> d = OrderedDict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
>>> d
collections.OrderedDict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
9). Quan trọng hơn, việc trở thành một lớp con dict cho phép các từ điển được sắp xếp có thể sử dụng được với các công cụ như json đòi hỏi phải có các đầu vào dict bằng cách kiểm tra isinstance(d, dict)

Có bất kỳ hạn chế phát sinh từ phân lớp dict?

Đúng. Vì API cho dicts khác trong Py2. x và Py3. x, API OrderedDict cũng phải khác. Vì vậy, Py2. 7 sẽ cần ghi đè iterkeys, itervalues ​​và iteritems

>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
0 có trả về một cặp khóa/giá trị cụ thể không?

Đúng. Nó bật ra khóa mới được chèn gần đây nhất và giá trị tương ứng của nó. Điều này tương ứng với hành vi LIFO thông thường được thể hiện bởi các cặp đẩy/bật truyền thống. Nó tương đương về mặt ngữ nghĩa với

>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
1. Việc triển khai thực tế hiệu quả hơn và bật trực tiếp từ danh sách các khóa được sắp xếp

OrderedDict có hỗ trợ lập chỉ mục, cắt và không?

Trên thực tế,

>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
2 không triển khai giao diện
>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
3. Thay vào đó, nó là một
>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
4 nhớ thứ tự chèn phím. Phần bổ sung giống như trình tự duy nhất là hỗ trợ cho
>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
5

Một ưu điểm khác của việc không cho phép lập chỉ mục là nó để ngỏ khả năng triển khai C nhanh bằng cách sử dụng danh sách được liên kết

OrderedDict có hỗ trợ các thứ tự sắp xếp thay thế như theo thứ tự bảng chữ cái không?

Không. Những người muốn các thứ tự sắp xếp khác nhau thực sự cần phải sử dụng một kỹ thuật khác. OrderedDict là tất cả về ghi lại thứ tự chèn. Nếu bất kỳ thứ tự nào khác được quan tâm, thì cấu trúc khác (như dbm trong bộ nhớ) có thể phù hợp hơn

OrderedDict hoạt động tốt như thế nào với mô-đun json, PyYAML và ConfigParser?

Đối với json, tin tốt là bộ mã hóa của json tôn trọng thứ tự lặp lại của OrderedDict

>>> items = [('one', 1), ('two', 2), ('three',3), ('four',4), ('five',5)]
>>> json.dumps(OrderedDict(items))
'{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}'

Trong Py2. 6, object_hook dành cho bộ giải mã json chuyển vào một từ điển đã được tạo sẵn để thứ tự bị mất trước khi móc đối tượng nhìn thấy nó. Sự cố này đang được khắc phục cho Python 2. 3/7. 1 bằng cách thêm một hook mới để giữ trật tự (xem https. //github. com/python/cpython/issues/49631 ). Với móc mới, trật tự có thể được giữ nguyên

>>> jtext = '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}'
>>> json.loads(jtext, object_pairs_hook=OrderedDict)
OrderedDict({'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})

Đối với PyYAML, một chuyến khứ hồi đầy đủ không có vấn đề gì

>>> ytext = yaml.dump(OrderedDict(items))
>>> print ytext
!!python/object/apply:collections.OrderedDict
- - [one, 1]
  - [two, 2]
  - [three, 3]
  - [four, 4]
  - [five, 5]

>>> yaml.load(ytext)
OrderedDict({'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})

Đối với mô-đun ConfigParser, việc quay vòng cũng không gặp sự cố. Các lệnh tùy chỉnh đã được thêm vào trong Py2. 6 đặc biệt để hỗ trợ từ điển theo thứ tự

>>> config = ConfigParser(dict_type=OrderedDict)
>>> config.read('myconfig.ini')
>>> config.remove_option('Log', 'error')
>>> config.write(open('myconfig.ini', 'w'))

OrderedDict xử lý kiểm tra bình đẳng như thế nào?

So sánh hai từ điển theo thứ tự ngụ ý rằng bài kiểm tra sẽ nhạy cảm với thứ tự để danh sách

>>> d['spam'] = 'eggs'
>>> d.keys()
['a', 'c', 'foo', 'spam']
>>> d.values()
['b', 'd', 'bar', 'eggs']
>>> d.items()
[('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
6

Khi các lệnh được đặt hàng được so sánh với các Ánh xạ khác, phép so sánh không phân biệt thứ tự của chúng được sử dụng. Điều này cho phép thay thế các từ điển có thứ tự ở bất kỳ nơi nào sử dụng từ điển thông thường

Định dạng __repr__ sẽ duy trì trật tự như thế nào trong chuyến đi khứ hồi repr/eval?

OrderedDict([('a', 1), ('b', 2)])

Sự đánh đổi của các cấu trúc dữ liệu cơ bản có thể là gì?

  • Giữ một danh sách các khóa được sắp xếp nhanh chóng cho tất cả các thao tác ngoại trừ __delitem__() trở thành bài tập O(n). Cấu trúc dữ liệu này dẫn đến mã rất đơn giản và ít không gian bị lãng phí
  • Giữ một từ điển riêng để ghi số thứ tự chèn làm cho mã phức tạp hơn một chút. Tất cả các thao tác cơ bản là O(1) nhưng hệ số không đổi được tăng lên đối với __setitem__() và __delitem__() nghĩa là mọi trường hợp sử dụng sẽ phải trả phí cho việc tăng tốc này (vì tất cả quá trình tích lũy đều trải qua __setitem__). Ngoài ra, lần duyệt đầu tiên phát sinh chi phí phân loại
    >>> d['spam'] = 'eggs'
    >>> d.keys()
    ['a', 'c', 'foo', 'spam']
    >>> d.values()
    ['b', 'd', 'bar', 'eggs']
    >>> d.items()
    [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', 'eggs')]
    
    7 một lần. Chi phí lưu trữ gấp đôi so với cách tiếp cận theo danh sách khóa được sắp xếp
  • Phiên bản viết bằng C có thể sử dụng danh sách liên kết. Mã sẽ phức tạp hơn hai cách tiếp cận kia nhưng nó sẽ tiết kiệm không gian và sẽ giữ nguyên hiệu suất lớn như từ điển thông thường. Đó là cách nhanh nhất và tiết kiệm không gian nhất

Việc thực hiện với các bài kiểm tra và tài liệu là tại

Bản vá Dict đã đặt hàng

Phiên bản đề xuất có một số giá trị

  • Tuân thủ nghiêm ngặt API MutableMapping và không có phương pháp mới để đường cong học tập gần bằng không. Nó chỉ đơn giản là một từ điển ghi nhớ thứ tự chèn
  • Nói chung hiệu suất tốt. Thời gian big-oh giống như từ điển thông thường ngoại trừ việc xóa khóa là O(n)

Các triển khai khác của lệnh được đặt hàng trong các dự án Python hoặc thư viện độc lập khác nhau, đã truyền cảm hứng cho API được đề xuất ở đây, là

  • odict trong Python
  • mô-đun odict
  • ra lệnhdict (triển khai C của mô-đun odict)
  • Ổn địnhDict
  • OrderedDict của Armin Rigo

Với sự sẵn có của một lệnh được đặt hàng trong thư viện tiêu chuẩn, các thư viện khác có thể tận dụng lợi thế đó. Ví dụ: ElementTree có thể trả về các odict trong tương lai giữ nguyên thứ tự thuộc tính của tệp nguồn

Python Dicts có được sắp xếp theo mặc định không?

# và sắp xếp từ điển. Ghi chú. Bắt đầu từ Python 3. 7, thứ tự chèn của từ điển Python được đảm bảo .

Các mục dict có trả lại theo thứ tự không?

Các phím sẽ xuất hiện theo thứ tự tùy ý . Các phương pháp dict. keys() và dict. các giá trị () trả về danh sách các khóa hoặc giá trị một cách rõ ràng.