Đối tượng xem từ điển là gì?


158

Trong python 2.7, chúng ta có các phương thức xem từ điển có sẵn.

Bây giờ, tôi biết các ưu và nhược điểm sau:

  • dict.items()(và values, keys): trả về một danh sách, vì vậy bạn thực sự có thể lưu trữ kết quả và
  • dict.iteritems() (và tương tự): trả về một trình tạo, do đó bạn có thể lặp qua từng giá trị được tạo từng cái một.

Là gì dict.viewitems()(và các loại tương tự) cho? Lợi ích của họ là gì? Làm thế nào nó hoạt động? Rốt cuộc là gì?

Tôi đọc rằng chế độ xem luôn phản ánh những thay đổi từ từ điển. Nhưng làm thế nào để nó hành xử từ quan điểm hoàn hảo và bộ nhớ? Các pro và nhược điểm là gì?

Câu trả lời:


157

Lượt xem từ điển về cơ bản là tên của chúng nói: lượt xem đơn giản giống như một cửa sổ trên các khóa và giá trị (hoặc vật phẩm) của từ điển. Đây là một đoạn trích từ tài liệu chính thức cho Python 3:

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(Sử dụng tương đương Python 2 dishes.viewkeys()dishes.viewvalues() .)

Ví dụ này cho thấy đặc tính động của các khung nhìn : khung nhìn các phím là không phải là bản sao của các phím tại một thời điểm nhất định, mà là một cửa sổ đơn giản hiển thị cho bạn các phím; nếu chúng được thay đổi, thì những gì bạn nhìn thấy qua cửa sổ cũng thay đổi. Tính năng này có thể hữu ích trong một số trường hợp (ví dụ: người ta có thể làm việc với chế độ xem các phím trong nhiều phần của chương trình thay vì tính toán lại danh sách các khóa hiện tại mỗi khi chúng cần) Lưu ý rằng nếu các khóa từ điển bị sửa đổi trong khi lặp qua chế độ xem, cách hành xử của trình lặp không được xác định rõ, điều này có thể dẫn đến lỗi .

Một lợi thế là nhìn vào các phím chỉ sử dụng một lượng bộ nhớ nhỏ và cố định nhỏ và cố định và yêu cầu thời gian xử lý nhỏ và cố định , vì mặt khác không có danh sách các khóa (Python 2, thường không cần thiết tạo ra một danh sách mới, như được trích dẫn bởi Rajendran T, nó chiếm bộ nhớ và thời gian theo tỷ lệ thuận với độ dài của danh sách). Để tiếp tục tương tự cửa sổ, nếu bạn muốn nhìn thấy một phong cảnh đằng sau bức tường, bạn chỉ cần tạo một lỗ mở trong đó (bạn xây dựng một cửa sổ); sao chép các khóa vào một danh sách sẽ tương ứng với việc thay thế một bản sao của phong cảnh trên tường của bạn, bản sao đó mất thời gian, không gian và không tự cập nhật.

Tóm lại, các khung nhìn chỉ đơn giản là các khung nhìn (cửa sổ) trên từ điển của bạn, hiển thị nội dung của từ điển ngay cả sau khi nó thay đổi. Họ cung cấp các tính năng khác với các danh sách: danh sách các khóa chứa bản sao các khóa từ điển tại một thời điểm nhất định, trong khi chế độ xem là động và nhanh hơn để có được, vì nó không phải sao chép bất kỳ dữ liệu nào ( khóa hoặc giá trị) để được tạo.


6
+1. Ok, nó khác với việc truy cập trực tiếp vào danh sách các phím bên trong như thế nào? Là nhanh hơn, chậm hơn? Bộ nhớ hiệu quả hơn? Hạn chế? Nếu bạn có thể đọc và chỉnh sửa nó, nó có cảm giác giống hệt như có một tham chiếu đến danh sách này.
e-satis

3
Cảm ơn. Vấn đề là các khung nhìn quyền truy cập của bạn vào "danh sách các khóa bên trong" (lưu ý rằng "danh sách các khóa" này không phải là danh sách Python, nhưng chính xác là một khung nhìn). Lượt xem hiệu quả hơn về bộ nhớ so với danh sách các khóa (hoặc giá trị hoặc vật phẩm) của Python 2, vì chúng không sao chép bất cứ thứ gì; chúng thực sự giống như "một tham chiếu đến danh sách các khóa" (cũng lưu ý rằng "tham chiếu đến danh sách" thực sự được gọi đơn giản là một danh sách, trong Python, vì danh sách là các đối tượng có thể thay đổi). Cũng lưu ý rằng bạn không thể chỉnh sửa trực tiếp các lượt xem: thay vào đó, bạn vẫn chỉnh sửa từ điển và các lượt xem phản ánh các thay đổi của bạn ngay lập tức.
Eric O Lebigot

3
Ok, tôi chưa rõ về việc thực hiện, nhưng đó là câu trả lời tốt nhất cho đến nay.
e-satis

2
Cảm ơn. Thật vậy, câu trả lời này chủ yếu là về ngữ nghĩa của quan điểm. Tôi không có thông tin về việc triển khai chúng trong CPython, nhưng tôi đoán rằng về cơ bản, một khung nhìn là một con trỏ tới (các) cấu trúc (khóa và / hoặc giá trị) bên phải và các cấu trúc đó là một phần của chính đối tượng từ điển.
Eric O Lebigot

5
Tôi nghĩ rằng đáng để chỉ ra rằng mã ví dụ trong bài viết này là từ python3 và không phải là những gì tôi nhận được trong python2.7.
snth

21

Như bạn đã đề cập dict.items()trả về một bản sao của danh sách các cặp (khóa, giá trị) của từ điển gây lãng phí và dict.iteritems()trả về một trình vòng lặp qua các cặp (khóa, giá trị) của từ điển.

Bây giờ hãy lấy ví dụ sau để thấy sự khác biệt giữa một interator of dict và view of dict

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Trong khi đó, một khung nhìn chỉ đơn giản cho bạn thấy những gì trong dict. Nó không quan tâm nếu nó thay đổi:

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

Một khung nhìn đơn giản là một cái mà từ điển trông giống như bây giờ. Sau khi xóa một mục .items()sẽ bị lỗi thời và .iteritems()sẽ có lỗi.


Ví dụ tuyệt vời, cảm ơn. Mặc dù, nên là v = d.items () chứ không phải v - d.viewitems ()
rix

1
Câu hỏi là về Python 2.7, vì vậy viewitems()thực sự chính xác (items() đưa ra một cách nhìn chính xác trong Python 3 ).
Eric O Lebigot

Tuy nhiên, một khung nhìn không thể được sử dụng để lặp lại từ điển trong khi sửa đổi nó.
Ioannis Filippidis

18

Chỉ cần đọc tài liệu tôi mới có ấn tượng này:

  1. Lượt xem là "giả giống như tập hợp", ở chỗ chúng không hỗ trợ lập chỉ mục, vì vậy những gì bạn có thể làm với chúng là kiểm tra tư cách thành viên và lặp lại trên chúng (vì các khóa có thể băm và duy nhất, các lượt xem khóa và mục có nhiều hơn " set-like "ở chỗ chúng không chứa các bản sao).
  2. Bạn có thể lưu trữ chúng và sử dụng chúng nhiều lần, như các phiên bản danh sách.
  3. Bởi vì chúng phản ánh từ điển cơ bản, bất kỳ thay đổi nào trong từ điển sẽ thay đổi chế độ xem và gần như chắc chắn sẽ thay đổi thứ tự lặp . Vì vậy, không giống như các phiên bản danh sách, chúng không "ổn định".
  4. Bởi vì chúng phản ánh từ điển cơ bản, chúng gần như chắc chắn là các đối tượng proxy nhỏ; sao chép các khóa / giá trị / vật phẩm sẽ yêu cầu họ xem từ điển gốc bằng cách nào đó và sao chép nó nhiều lần khi có thay đổi, đó sẽ là một triển khai vô lý. Vì vậy, tôi sẽ mong đợi rất ít bộ nhớ trên đầu, nhưng truy cập chậm hơn một chút so với trực tiếp vào từ điển.

Vì vậy, tôi đoán rằng usecase khóa là nếu bạn giữ một từ điển xung quanh và lặp đi lặp lại trên các khóa / vật phẩm / giá trị của nó với các sửa đổi ở giữa. Bạn chỉ có thể sử dụng một khung nhìn thay thế, biến for k, v in mydict.iteritems():thành for k, v in myview:. Nhưng nếu bạn chỉ lặp lại từ điển một lần, tôi nghĩ các phiên bản lặp vẫn thích hợp hơn.


2
+1 để phân tích các ưu và nhược điểm từ một vài thông tin chúng tôi nhận được.
e-satis

Nếu tôi tạo một trình vòng lặp qua chế độ xem, nó vẫn bị vô hiệu hóa mỗi khi từ điển thay đổi. Đó là vấn đề tương tự như với một trình vòng lặp trên chính từ điển (ví dụ iteritems()). Vì vậy, quan điểm của những quan điểm này là gì? Khi tôi hạnh phúc khi có chúng?
Alfe

@Alfe Bạn nói đúng, đó là một vấn đề với việc lặp lại từ điển và các lượt xem không giúp ích gì cả. Giả sử bạn cần chuyển các giá trị của từ điển cho hàm. Bạn có thể sử dụng .values(), nhưng điều đó liên quan đến việc tạo một bản sao hoàn chỉnh dưới dạng một danh sách, có thể tốn kém. Có .itervalues()nhưng bạn không thể tiêu thụ chúng nhiều hơn một lần, vì vậy nó sẽ không hoạt động với mọi chức năng. Lượt xem không yêu cầu một bản sao đắt tiền, nhưng chúng vẫn hữu ích như một giá trị độc lập hơn so với trình lặp. Nhưng họ vẫn không có ý định giúp lặp lại và sửa đổi cùng một lúc (ở đó bạn thực sự muốn có một bản sao).
Ben

17

Các phương thức xem trả về một danh sách (không phải là bản sao của danh sách, so với .keys(), .items().values()), vì vậy nó nhẹ hơn, nhưng phản ánh nội dung hiện tại của từ điển.

Từ Python 3.0 - phương thức dict trả về lượt xem - tại sao?

Lý do chính là đối với nhiều trường hợp sử dụng, việc trả về một danh sách tách rời hoàn toàn là không cần thiết và lãng phí. Nó sẽ yêu cầu sao chép toàn bộ nội dung (có thể nhiều hoặc không nhiều).

Nếu bạn chỉ đơn giản muốn lặp lại các phím thì việc tạo một danh sách mới là không cần thiết. Và nếu bạn thực sự cần nó như một danh sách riêng (như một bản sao) thì bạn có thể dễ dàng tạo danh sách đó từ chế độ xem.


6
Các phương thức xem trả về các đối tượng xem, không phù hợp với giao diện danh sách.
Matthew Trevor

5

Lượt xem cho phép bạn truy cập cấu trúc dữ liệu bên dưới mà không cần sao chép nó. Bên cạnh việc năng động thay vì tạo ra một danh sách, một trong những cách sử dụng hữu ích nhất của họ là inkiểm tra. Giả sử bạn muốn kiểm tra xem một giá trị có nằm trong lệnh hay không (có thể là khóa hoặc giá trị).

Tùy chọn một là tạo danh sách các phím bằng cách sử dụng dict.keys(), điều này hoạt động nhưng rõ ràng sẽ tiêu tốn nhiều bộ nhớ hơn. Nếu dict rất lớn? Điều đó sẽ lãng phí.

Với viewsbạn có thể lặp lại cấu trúc dữ liệu thực tế, không có danh sách trung gian.

Hãy sử dụng các ví dụ. Tôi có một lệnh với 1000 khóa gồm các chuỗi và chữ số ngẫu nhiên và klà khóa tôi muốn tìm

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

Như bạn có thể thấy, viewđối tượng lặp lại giúp tăng hiệu suất rất lớn, giảm chi phí bộ nhớ cùng một lúc. Bạn nên sử dụng chúng khi bạn cần thực hiện Setnhư các thao tác.

Lưu ý : Tôi đang chạy trên Python 2.7


Trong python> = 3, tôi tin rằng .keys()trả về một chế độ xem theo mặc định. Có thể muốn kiểm tra lại tho
Yolo Voe

1
Bạn đúng. Python 3+ sử dụng nhiều đối tượng xem thay vì danh sách, nó hiệu quả hơn về bộ nhớ
Chen A.

1
Các kết quả thời gian này rất đáng nói, nhưng kiểm tra xem kmột trong những khóa của từ điển large_dcó nghĩa là được thực hiện với k in large_d, trong Python, về cơ bản có thể nhanh như sử dụng chế độ xem (nói cách khác, k in large_d.keys()không phải là Pythonic và nên tránh tránh như là k in large_d.viewkeys()).
Eric O Lebigot

Cảm ơn bạn đã cung cấp một ví dụ vững chắc, hữu ích. k in large_dthực sự nhanh hơn đáng kể so với k in large_d.viewkeys(), vì vậy có lẽ nên tránh, nhưng điều này có ý nghĩa k in large_d.viewvalues().
ness101
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.