Trong Python, làm cách nào để lặp lại từ điển theo thứ tự khóa được sắp xếp?


211

Có một chức năng hiện có kết thúc sau đây, trong đó dlà một từ điển:

return d.iteritems()

trả về một trình vòng lặp chưa sắp xếp cho một từ điển đã cho. Tôi muốn trả về một iterator đi qua các mục được sắp xếp theo khóa . Làm thế nào để làm điều đó?

Câu trả lời:


171

Không thử nghiệm điều này rất nhiều, nhưng hoạt động trong Python 2.5.2.

>>> d = {"x":2, "h":15, "a":2222}
>>> it = iter(sorted(d.iteritems()))
>>> it.next()
('a', 2222)
>>> it.next()
('h', 15)
>>> it.next()
('x', 2)
>>>

Nếu bạn đã quen làm for key, value in d.iteritems(): ...thay vì lặp, điều này vẫn sẽ hoạt động với giải pháp ở trên

>>> d = {"x":2, "h":15, "a":2222}
>>> for key, value in sorted(d.iteritems()):
>>>     print(key, value)
('a', 2222)
('h', 15)
('x', 2)
>>>

Với Python 3.x, sử dụng d.items()thay vì d.iteritems()trả về một trình vòng lặp.


29
sử dụng .items()thay vì iteritems(): như @Claudiu đã nói, iteritems không hoạt động với Python 3.x, nhưng items()có sẵn từ Python 2.6.
Remi

40
Điều này không rõ ràng. Trong thực tế, items()tạo một danh sách và do đó sử dụng bộ nhớ, trong khi iteritems()về cơ bản không sử dụng bộ nhớ. Những gì để sử dụng chủ yếu phụ thuộc vào kích thước của từ điển. Hơn nữa, công cụ chuyển đổi Python 2 sang Python 3 2to3tự động ( ) tự động đảm nhiệm việc chuyển đổi từ iteritems()sang items(), do đó không cần phải lo lắng về điều này.
Eric O Lebigot

5
@HowerHell sử dụng collections.OrderedDictsau đó bạn sắp xếp một lần và nhận các mục theo thứ tự sắp xếp luôn.
Mark Crawiston

9
Nhưng @EOL, ngay cả khi iteritems()không sử dụng bộ nhớ, mọi thứ đều phải được kéo vào bộ nhớ sorted(), vì vậy không có sự khác biệt giữa việc sử dụng items()iteritems()ở đây là bộ nhớ khôn ngoan.
Richard

8
@Richard: Mặc dù đúng là tất cả các yếu tố phải được kéo vào bộ nhớ, chúng được lưu trữ hai lần với items()(trong danh sách được trả về items()và trong danh sách được sắp xếp) và chỉ một lần với iteritems()(chỉ trong danh sách được sắp xếp).
Eric O Lebigot

83

Sử dụng sorted()chức năng:

return sorted(dict.iteritems())

Nếu bạn muốn một trình lặp thực tế trên các kết quả được sắp xếp, vì sorted()trả về một danh sách, hãy sử dụng:

return iter(sorted(dict.iteritems()))

Điều đó không thành công đối với tôi: <type 'exceptions.TypeError'>: iter () đã trả về non-iterator của loại 'list'
mike

Điều đó có thể là do bạn sử dụng "dict" làm tên biến. "Dict" thực sự là tên loại từ điển. Chỉ cần sử dụng một tên khác như "mydict" ở đây và voila.
utku_karatas

1
Vẫn không làm việc. Bạn có được sắp xếp tích cực () trả về một trình vòng lặp khác, trái ngược với danh sách thông thường không?
mike

ngoại lệ này xảy ra khi nào và ở đâu? bạn có thể lặp lại một danh sách mà không gặp vấn đề gì

1
Đồng ý, nhảy. Tôi không nghĩ rằng mình đã từng gọi .next () trực tiếp trừ khi bỏ qua các dòng trong tệp. Giải pháp iter (sort (dict.iteritems ())) của chúng tôi cuối cùng tạo ra một bản sao của toàn bộ dict trong bộ nhớ ở giai đoạn "

39

Các khóa của một dict được lưu trữ trong một hashtable sao cho đó là 'thứ tự tự nhiên' của chúng, tức là psuedo-Random. Bất kỳ thứ tự khác là một khái niệm của người tiêu dùng của dict.

sort () luôn trả về một danh sách, không phải là dict. Nếu bạn truyền cho nó một dict.items () (tạo ra danh sách các bộ dữ liệu), nó sẽ trả về một danh sách các bộ dữ liệu [(k1, v1), (k2, v2), ...] có thể được sử dụng trong một vòng lặp theo một cách rất giống như một dict, nhưng dù sao nó cũng không phải là một dict !

foo = {
    'a':    1,
    'b':    2,
    'c':    3,
    }

print foo
>>> {'a': 1, 'c': 3, 'b': 2}

print foo.items()
>>> [('a', 1), ('c', 3), ('b', 2)]

print sorted(foo.items())
>>> [('a', 1), ('b', 2), ('c', 3)]

Sau đây cảm thấy như một lệnh trong một vòng lặp, nhưng không phải, đó là một danh sách các bộ dữ liệu được giải nén vào k, v:

for k,v in sorted(foo.items()):
    print k, v

Khá tương đương với:

for k in sorted(foo.keys()):
    print k, foo[k]

Được rồi, nhưng tôi không muốn Dict hoặc List, tôi muốn Iterator. Làm thế nào để tôi ép buộc nó trở thành một Iterator?
mike

2
sorted(foo.keys())tốt hơn là tương đương sorted(foo), vì từ điển trả về các khóa của chúng khi lặp đi lặp lại (với lợi thế là không bị buộc phải tạo foo.keys()danh sách trung gian, có thể là tùy thuộc vào cách sorted()triển khai cho các lần lặp).
Eric O Lebigot

Tự hỏi cái nào tốt hơn cho tốc độ và / hoặc bộ nhớ k in sorted(foo.keys()):kéo khóa hoặc for k,v in sorted(foo.items()):trả về một bản sao của cặp danh sách từ điển mà tôi đoánsorted(foo.keys())
CrandellWS

1
@CrandellWS: Cách tốt nhất để trả lời những câu hỏi thời gian là với Python timeit module.
Peter Rowell

1
@frank - Trả lời ngắn: Không. Dict là một mảng với khóa thực tế là hàm băm của giá trị của khóa được cung cấp. Mặc dù một số triển khai có thể khá dễ đoán và một số thậm chí có thể thực hiện hợp đồng này, tôi không tính khi nói đến việc đặt hàng băm. Xem bài đăng này để biết thêm về hành vi 3.6+. Đặc biệt lưu ý câu trả lời đầu tiên.
Peter Rowell

31

Câu trả lời của Greg là đúng. Lưu ý rằng trong Python 3.0 bạn sẽ phải làm

sorted(dict.items())

như iteritemssẽ biến mất


Điều đó không thành công đối với tôi: <type 'exceptions.TypeError'>: iter () đã trả về non-iterator của loại 'list'
mike

3
"Đừng sử dụng ô tô vì trong tương lai chúng ta sẽ có ván trượt"
JJ

7

Bây giờ bạn cũng có thể sử dụng OrderedDicttrong Python 2.7:

>>> from collections import OrderedDict
>>> d = OrderedDict([('first', 1),
...                  ('second', 2),
...                  ('third', 3)])
>>> d.items()
[('first', 1), ('second', 2), ('third', 3)]

Tại đây bạn có trang mới cho phiên bản 2.7 và API OrderedDict .


Điều đó sẽ trả về khóa, các giá trị theo thứ tự chúng được chèn - không theo thứ tự được sắp xếp (nghĩa là chữ cái).
Tony Suffolk 66

5

Nói chung, người ta có thể sắp xếp một dict như vậy:

for k in sorted(d):
    print k, d[k]

Đối với trường hợp cụ thể trong câu hỏi, có "thả thay thế" cho d.iteritems (), hãy thêm một hàm như:

def sortdict(d, **opts):
    # **opts so any currently supported sorted() options can be passed
    for k in sorted(d, **opts):
        yield k, d[k]

và do đó, dòng kết thúc thay đổi từ

return dict.iteritems()

đến

return sortdict(dict)

hoặc là

return sortdict(dict, reverse = True)

5
>>> import heapq
>>> d = {"c": 2, "b": 9, "a": 4, "d": 8}
>>> def iter_sorted(d):
        keys = list(d)
        heapq.heapify(keys) # Transforms to heap in O(N) time
        while keys:
            k = heapq.heappop(keys) # takes O(log n) time
            yield (k, d[k])


>>> i = iter_sorted(d)
>>> for x in i:
        print x


('a', 4)
('b', 9)
('c', 2)
('d', 8)

Phương pháp này vẫn có một loại O (N log N), tuy nhiên, sau một heapify tuyến tính ngắn, nó mang lại các mục theo thứ tự sắp xếp, làm cho nó hiệu quả hơn về mặt lý thuyết khi bạn không luôn cần toàn bộ danh sách.



3

đã sắp xếp trả về một danh sách, do đó lỗi của bạn khi bạn cố gắng lặp lại nó, nhưng vì bạn không thể ra lệnh, bạn sẽ phải xử lý một danh sách.

Tôi không biết bối cảnh lớn hơn của mã của bạn là gì, nhưng bạn có thể thử thêm một trình lặp vào danh sách kết quả. như thế này có lẽ?:

return iter(sorted(dict.iteritems()))

tất nhiên bạn sẽ nhận lại các bộ dữ liệu ngay bây giờ vì được sắp xếp đã biến chính tả của bạn thành một danh sách các bộ dữ liệu

ví dụ: nói dict của bạn là: được {'a':1,'c':3,'b':2} sắp xếp biến nó thành một danh sách:

[('a',1),('b',2),('c',3)]

vì vậy khi bạn thực sự lặp lại danh sách, bạn sẽ quay lại (trong ví dụ này) một bộ gồm một chuỗi và một số nguyên, nhưng ít nhất bạn sẽ có thể lặp lại nó.


2

Giả sử bạn đang sử dụng CPython 2.x và có một từ điển lớn mydict, thì việc sử dụng sort (mydict) sẽ bị chậm vì sắp xếp xây dựng một danh sách sắp xếp các khóa của mydict.

Trong trường hợp đó, bạn có thể muốn xem gói orderdict của tôi bao gồm triển khai C sorteddicttrong C. Đặc biệt nếu bạn phải xem qua danh sách các khóa được sắp xếp nhiều lần ở các giai đoạn khác nhau (ví dụ: số phần tử) của thời gian từ điển.

http://anthon.home.xs4all.nl/Python/ordereddict/

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.