Làm thế nào để giữ các khóa / giá trị theo thứ tự như đã khai báo?


320

Tôi có một từ điển mà tôi đã khai báo theo một thứ tự cụ thể và muốn giữ nó theo thứ tự đó mọi lúc. Các khóa / giá trị thực sự không thể được giữ theo thứ tự dựa trên giá trị của chúng, tôi chỉ muốn nó theo thứ tự mà tôi đã khai báo.

Vì vậy, nếu tôi có từ điển:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Sẽ không theo thứ tự đó nếu tôi xem nó hoặc lặp qua nó, có cách nào để đảm bảo Python sẽ giữ thứ tự rõ ràng mà tôi đã khai báo các khóa / giá trị không?

Câu trả lời:


229

Từ Python 3.6 trở đi, dictloại tiêu chuẩn duy trì thứ tự chèn theo mặc định.

Xác định

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

sẽ dẫn đến một từ điển với các khóa theo thứ tự được liệt kê trong mã nguồn.

Điều này đạt được bằng cách sử dụng một mảng đơn giản với các số nguyên cho bảng băm thưa thớt, trong đó các số nguyên đó lập chỉ mục vào một mảng khác lưu trữ các cặp khóa-giá trị (cộng với hàm băm được tính toán). Mảng sau đó chỉ xảy ra để lưu trữ các mục theo thứ tự chèn và toàn bộ kết hợp thực sự sử dụng ít bộ nhớ hơn so với triển khai được sử dụng trong Python 3.5 và trước đó. Xem bài viết ý tưởng ban đầu của Raymond Hettinger để biết chi tiết.

Trong 3.6 điều này vẫn được coi là một chi tiết thực hiện; xem tài liệu Có gì mới trong Python 3.6 :

Khía cạnh giữ trật tự của triển khai mới này được coi là một chi tiết triển khai và không nên dựa vào (điều này có thể thay đổi trong tương lai, nhưng mong muốn có triển khai chính tả mới này trong ngôn ngữ cho một vài bản phát hành trước khi thay đổi thông số ngôn ngữ để bắt buộc ngữ nghĩa duy trì trật tự cho tất cả các triển khai Python hiện tại và tương lai, điều này cũng giúp duy trì khả năng tương thích ngược với các phiên bản cũ hơn của ngôn ngữ trong đó thứ tự lặp ngẫu nhiên vẫn còn hiệu lực, ví dụ Python 3.5).

Python 3.7 nâng chi tiết triển khai này thành một đặc tả ngôn ngữ , do đó, bắt buộc phải dictgiữ trật tự trong tất cả các cài đặt Python tương thích với phiên bản đó hoặc mới hơn. Xem phần phát âm của BDFL .

Bạn vẫn có thể muốn sử dụng collections.OrderedDict()lớp trong một số trường hợp nhất định, vì nó cung cấp một số chức năng bổ sung trên đầu trang của dictloại tiêu chuẩn . Chẳng hạn như có thể đảo ngược (điều này mở rộng cho các đối tượng xem ) và hỗ trợ sắp xếp lại (thông qua move_to_end()phương thức ).


Thật không may, điều này không liên quan đến việc tạo từ điển . Từ điển được chỉ định như từ điển trong ví dụ trong mã không được đảm bảo để duy trì cùng một thứ tự. Điều này có nghĩa là bạn cần tạo một dict trống và chèn thủ công từng phần tử nếu bạn muốn kiểm soát thứ tự.
ness101

8
@ naught101: không, điều này không áp dụng cho việc tạo dict. Trong Python 3.7 trở lên, sử dụng màn hình chính tả như trong câu hỏi được đảm bảo liệt kê các khóa đó theo thứ tự . Các cặp khóa-giá trị như được viết được chèn từ trái sang phải, bởi vì thông số ngôn ngữ đảm bảo rằng : Nếu một chuỗi các cặp khóa / mốc được phân tách bằng dấu phẩy được đưa ra, chúng được đánh giá từ trái sang phải để xác định các mục của từ điển . Các dict()tài liệu thậm chí bao gồm một ví dụ.
Martijn Pieters

175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

chứa đựng

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Nếu các giá trị là True(hoặc bất kỳ đối tượng bất biến nào khác), bạn cũng có thể sử dụng:

OrderedDict.fromkeys(words, True)

2
Tất nhiên, đáng chú ý là phần 'bất biến' không phải là một quy tắc cứng và nhanh mà Python sẽ thực thi - đó là "ý tưởng" duy nhất của nó.
lvc

11
lưu ý rằng các giải pháp như: sẽ không OrderedDict(FUTURE=[], TODAY=[], PAST=[])hoạt động, khi được đề cập đến aproach: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])sẽ giữ trật tự.
andilabs

2
@andi Tôi gặp một vấn đề khác, khi sử dụng jsonify, OrderedDict dường như mất trật tự khi tạo dữ liệu json. Dù sao bạn cũng có thể giải quyết vấn đề này?
tyan

github.com/pallets/flask/issues/974 điều này có thể được sử dụng để giải quyết vấn đề ..
tyan

5
Python3.7 hiện có dict được đặt hàng theo mặc định. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat

167

Thay vì giải thích phần lý thuyết, tôi sẽ đưa ra một ví dụ đơn giản.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}

16
Có cách nào để gán khối lượng OrderedDict giống như kiểu Dict không?
tyan

2
OrderedDictthực sự giải quyết được vấn đề, nhưng ... trong ví dụ cụ thể này, bạn nhận được kết quả chính xác bằng cách sử dụng một từ điển tiêu chuẩn
Tonechas

2
@Tonechas: Tôi vừa thử ví dụ với một từ điển chuẩn và đã nhận được {'aol': 1, 'foo': 3}Vì vậy tôi nghĩ đó là một ví dụ minh họa tốt.
twasbrillig

4
Có một bài học cho tất cả mọi người: người ta đã phát hiện ra (tôi nghĩ xung quanh bản phát hành 2.4) rằng việc băm có thể dự đoán được của Python có thể làm phát sinh các lỗ hổng bảo mật , vì vậy bây giờ không có gì đảm bảo rằng ngay cả hai lần chạy mã khác nhau sẽ có cùng thứ tự trong một tiêu chuẩn sai khiến
Holdenweb

1
@tyan bạn có thể gọi OrderedDict.update()với một cặp lặp có chứa khóa-giá trị : d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen

37

Lưu ý rằng câu trả lời này áp dụng cho các phiên bản python trước python3.7. CPython 3.6 duy trì thứ tự chèn trong hầu hết các trường hợp như một chi tiết triển khai. Bắt đầu từ Python3.7 trở đi, đã tuyên bố rằng việc triển khai PHẢI duy trì thứ tự chèn phải tuân thủ.


từ điển python là không có thứ tự. Nếu bạn muốn có một từ điển được đặt hàng, hãy thử bộ sưu tập.OrderedDict .

Lưu ý rằng OrderedDict đã được đưa vào thư viện chuẩn trong python 2.7. Nếu bạn có phiên bản cũ hơn của python, bạn có thể tìm công thức cho các từ điển được đặt hàng trên ActiveState .


xem bài viết của martijn ở trên. Từ python 3.6 trở đi, dict hỗ trợ thứ tự chèn.
tpk

13

Từ điển sẽ sử dụng một thứ tự giúp tìm kiếm hiệu quả và bạn không thể thay đổi điều đó,

Bạn chỉ có thể sử dụng một danh sách các đối tượng (một phần tử gồm 2 phần tử trong trường hợp đơn giản hoặc thậm chí là một lớp) và nối các mục vào cuối. Sau đó, bạn có thể sử dụng tìm kiếm tuyến tính để tìm các mục trong đó.

Ngoài ra, bạn có thể tạo hoặc sử dụng cấu trúc dữ liệu khác được tạo với mục đích duy trì trật tự.


Từ điển sẽ sử dụng một thứ tự giúp tìm kiếm hiệu quả Cuối cùng, có người đã chỉ ra nó.
scharette

7

Tôi đã xem qua bài đăng này trong khi cố gắng tìm ra cách để OrderedDict hoạt động. PyDev cho Eclipse hoàn toàn không thể tìm thấy OrderedDict, vì vậy tôi đã quyết định tạo ra một bộ các giá trị chính trong từ điển của mình vì tôi muốn chúng được đặt hàng. Khi tôi cần xuất danh sách của mình, tôi chỉ lặp qua các giá trị của bộ dữ liệu và cắm 'khóa' lặp từ bộ dữ liệu vào từ điển để lấy các giá trị của mình theo thứ tự tôi cần.

thí dụ:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

Đó là một chút rườm rà, nhưng tôi bị ép thời gian và đó là cách giải quyết mà tôi nghĩ ra.

lưu ý: danh sách các cách tiếp cận danh sách mà người khác đề xuất không thực sự có ý nghĩa đối với tôi, vì các danh sách được sắp xếp và lập chỉ mục (và cũng là một cấu trúc khác với từ điển).


Giải pháp tuyệt vời. Tôi sẽ sử dụng nó để viết json vào tập tin, luôn theo thứ tự.
Hrvoje T

6

Bạn thực sự không thể làm những gì bạn muốn với một từ điển. Bạn đã có từ điển d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}được tạo. Tôi thấy rằng không có cách nào để giữ theo thứ tự một khi nó đã được tạo. Những gì tôi đã làm là tạo một tệp json thay vì với đối tượng:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Tôi đã sử dụng:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

sau đó được sử dụng:

print json.dumps(r)

để xác minh.


1
Vậy tại sao không bắt đầu với OrderedDict từ danh sách? Tệp JSON không thực sự thêm bất cứ điều gì ở đây.
Martijn Pieters

Có, danh sách hữu ích hơn để giữ trật tự nhưng câu trả lời liên quan đến câu hỏi về việc đặt từ điển. Chỉ cần cho mọi người biết về những hạn chế của việc sử dụng từ điển và cung cấp cho họ một công việc khả thi nếu họ cần sử dụng từ điển vì một số lý do.
nealous3

1
Nhưng phần đó đã được bao phủ bởi các câu trả lời cũ hơn nhiều, kể từ năm 2012.
Martijn Pieters

3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])

vấn đề chính không còn là vấn đề nữa, đó là danh sách các bộ dữ liệu
Oleg

2

Một cách khác là sử dụng Pandas dataframevì nó đảm bảo thứ tự và vị trí chỉ mục của các mục trong cấu trúc giống như chính tả.


1

Nói chung, bạn có thể thiết kế một lớp học mà cư xử như một cuốn từ điển, chủ yếu được thực hiện phương pháp __contains__, __getitem__, __delitem__, __setitem__và một số chi tiết. Lớp đó có thể có bất kỳ hành vi nào bạn thích, ví dụ như sở hữu một trình vòng lặp được sắp xếp qua các khóa ...


1

nếu bạn muốn có một từ điển theo một thứ tự cụ thể, bạn cũng có thể tạo một danh sách các danh sách, trong đó mục đầu tiên sẽ là khóa và mục thứ hai sẽ là giá trị và sẽ giống như ví dụ này

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3

7
Đó không phải là "từ điển" vì bạn không thể tra cứu các mục bằng khóa của chúng mà không tìm kiếm trong toàn bộ bộ sưu tập (mất thời gian O (n)).
BHSPitMonkey

1
Vâng, nó không phải là một từ điển, nhưng, tùy thuộc vào tình huống, nó có thể cung cấp một giải pháp hợp lệ cho vấn đề của poster gốc.
SunSparc

anh ấy đã không nói chính xác chúng tôi muốn như thế nào, chỉ là muốn có thể đặt hàng chúng =), vì luôn có nhiều cách để làm một việc.
pelos

1

Tôi đã có một vấn đề tương tự khi phát triển một dự án Django. Tôi không thể sử dụng OrderedDict, vì tôi đang chạy một phiên bản cũ của python, vì vậy giải pháp là sử dụng lớp SortedDict của Django:

https://code.djangoproject.com/wiki/SortDict

ví dụ,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Lưu ý: Câu trả lời này có nguồn gốc từ năm 2011. Nếu bạn có quyền truy cập vào phiên bản Python 2.7 trở lên, thì bạn nên có quyền truy cập vào tiêu chuẩn hiện tại collections.OrderedDict, trong đó nhiều ví dụ đã được cung cấp bởi những người khác trong chuỗi này.


0

Bạn có thể làm điều tương tự mà tôi đã làm cho từ điển.

Tạo một danh sách và từ điển trống:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
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.