Що таке об'єкти перегляду словника?


158

У python 2.7 ми отримали доступні методи перегляду словника .

Тепер я знаю плюси та мінуси наступного:

  • dict.items()values, keys): повертає список, щоб ви могли фактично зберігати результат та
  • dict.iteritems() (тощо): повертає генератор, тож ви можете переглядати кожне значення, породжене по одному.

Для чого dict.viewitems()(тощо) подібні? Які їх переваги? Як це працює? Що таке точка зору?

Я читав, що представлення завжди відображає зміни зі словника. Але як це поводиться з точки зору парфуму та пам’яті? Які плюси і мінуси?

Відповіді:


157

Перегляди словників - це, по суті, те, що говорить їх назва: погляди просто як вікно на клавіші та значення (або елементи) словника. Ось уривок з офіційної документації для 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])

(Еквівалент Python 2 використовує dishes.viewkeys()і dishes.viewvalues().)

Цей приклад показує динамічний характер переглядів : перегляд клавіш - це не копія клавіш у заданий момент часу, а швидше вікно, яке показує вам клавіші; якщо вони змінені, то те, що ви бачите через вікно, теж змінюється. Ця функція може бути корисною в деяких обставинах (наприклад, можна працювати з видом на клавіші в декількох частинах програми, замість того, щоб перераховувати поточний список клавіш кожного разу, коли вони потрібні) - зауважте, що якщо словники змінюються в той час, як ітератор над поданням, як ітератор повинен поводитися, недостатньо визначений, що може призвести до помилок .

Одним з переваг є те , що , дивлячись на, скажімо, ключі використовують тільки невеликий фіксований обсяг пам'яті і вимагає невеликого і фіксованої кількості процесорного часу , так як немає створення списку ключів (Python 2, з іншого боку, часто необгрунтовано створює новий список, процитований Раджендраном Т, який займає пам'ять та час у кількості, пропорційній довжині списку). Щоб продовжити аналогію вікна, якщо ви хочете побачити пейзаж за стіною, ви просто зробите в ньому отвір (ви будуєте вікно); копіювання ключів у список відповідатиме замість того, щоб намалювати копію пейзажу на вашій стіні - копія займає час, простір та не оновлюється сама.

Підводячи підсумок, погляди - це просто… перегляди (вікна) вашого словника, які показують вміст словника навіть після його зміни. Вони пропонують функції, що відрізняються від списків: список клавіш містить копію словникових клавіш у певний момент часу, тоді як перегляд є динамічним і його набагато швидше отримати, оскільки не потрібно копіювати жодних даних ( ключі або значення) для їх створення.


6
+1. Гаразд, чим це відрізняється від прямого доступу до внутрішнього списку клавіш? Це швидше, повільніше? Більш ефективна пам'ять? Обмежений? Якщо ви можете прочитати та відредагувати, це буде точно так само, як посилання на цей список.
e-satis

3
Дякую. Вся справа в тому, що представлення даних - це ваш доступ до "внутрішнього списку клавіш" (зауважте, що цей "список ключів" не є списком Python, але це саме перегляд). Перегляди ефективніше пам’яті, ніж списки ключів (або значень чи елементів) Python 2, оскільки вони нічого не копіюють; вони справді схожі на "посилання на перелік клавіш" (також зауважте, що "посилання на список" насправді просто називається списком, в Python, оскільки списки є об'єктами, що змінюються). Також зауважте, що ви не можете безпосередньо редагувати представлення даних: натомість ви все-таки редагуєте словник, а представлення відображають ваші зміни негайно.
Ерік О Лебігот

3
Гаразд, я ще не зрозумілий щодо реалізації, але це найкраща відповідь поки що.
e-satis

2
Дякую. Дійсно, ця відповідь здебільшого стосується семантики поглядів. Я не маю інформації про їх реалізацію в CPython, але я б здогадався, що подання - це в основному вказівник на потрібну структуру (ключі) та / або значення), і що структури є частиною самого об'єкта словника.
Ерік О Лебігот

5
Я думаю, що варто зазначити, що приклад коду в цій публікації від python3 і не є тим, що я отримую в python2.7.
snth

21

Як ви вже згадували, dict.items()повертає копію списку словника (ключ, значення) пар, що марно і dict.iteritems()повертає ітератор над парами словника (ключ, значення).

Тепер візьмемо наступний приклад, щоб побачити різницю між інтегратором дикту та поглядом на дікт

>>> 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

Тоді як огляд просто показує, що є в дакті. Байдуже, чи змінився він:

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

Перегляд - це просто те, як виглядає словник зараз. Після видалення запису .items()було б застарілим і .iteritems()виникла б помилка.


Чудовий приклад, дякую. Хоча, має бути v = d.items (), а не v - d.viewitems ()
rix

1
Питання стосується Python 2.7, так viewitems()це насправді правильно ( items()правильно дає думку в Python 3 ).
Ерік О Лебігот

Однак перегляд не можна використовувати для повторення словника під час його модифікації.
Іоанніс Філіппідіс

18

Якраз від читання документів у мене виникає таке враження:

  1. Перегляди є "псевдонабірними", оскільки вони не підтримують індексацію, тому те, що ви можете зробити з ними, - це перевірка на членство та повторення над ними (оскільки ключі є доступними та унікальними, перегляди ключів та елементів більше " на зразок ", оскільки вони не містять дублікатів).
  2. Ви можете зберігати їх і використовувати їх кілька разів, як і список версій.
  3. Оскільки вони відображають основний словник, будь-яка зміна словника змінить вигляд і майже напевно змінить порядок ітерації . Отже, на відміну від версій списку, вони не "стабільні".
  4. Оскільки вони відображають основний словник, вони майже напевно є невеликими проксі-об'єктами; копіювання ключів / значень / елементів вимагає, щоб вони якось переглядали оригінальний словник і копіювали його кілька разів, коли відбудуться зміни, що було б абсурдною реалізацією. Тож я б очікував, що надто мало пам'яті накладні, але доступ буде трохи повільніше, ніж безпосередньо до словника.

Тож я здогадуюсь, що ключовий регістр - це якщо ви зберігаєте словник і повторно повторюєте його ключі / елементи / значення з модифікаціями між ними. Ви можете просто використовувати перегляд, замість цього перетворившись for k, v in mydict.iteritems():на for k, v in myview:. Але якщо ви просто повторюєте словник один раз, я думаю, що версії iter все ще є кращими.


2
+1 для аналізу плюсів і мінусів з кількох отриманих нами даних.
e-satis

Якщо я створюю ітератор для подання, він все одно втрачає свою силу, коли змінює словник. Це та сама проблема, як з ітератором над самим словником (наприклад iteritems()). Тож у чому сенс цих поглядів? Коли я щасливий їх мати?
Альфе

@Alfe Ви маєте рацію, що проблема з ітерацією словника, і перегляди це зовсім не допомагають. Скажіть, вам потрібно передати значення функції словника функції. Ви можете використовувати .values(), але це передбачає створення цілої копії як списку, що може дорого коштувати. Є, .itervalues()але ви не можете споживати їх більше одного разу, тому це не працюватиме з кожною функцією. Перегляди не вимагають дорогої копії, але вони все ще корисніші як окрема цінність, ніж ітератор. Але вони все ще не мають наміру допомогти з ітерацією та зміною одночасно (там ви дійсно хочете копії).
Бен

17

Методи вид повертає список (НЕ копію списку, по порівнянні з .keys(), .items()а .values()), так що це більш легкий, але відображає поточний зміст словника.

З Python 3.0 - dict методи повернення поглядів - чому?

Основна причина полягає в тому, що для багатьох випадків використання повернення повністю відокремленого списку є непотрібним та марним. Було б потрібно копіювати весь вміст (який може бути чи багато, а не багато).

Якщо ви просто хочете перебрати ключі, тоді створювати новий список не потрібно. І якщо він вам справді потрібен як окремий список (як копія), ви можете легко створити цей список з подання.


6
Методи перегляду повертають об'єкти перегляду, які не відповідають інтерфейсу списку.
Меттью Тревор

5

Перегляди дозволяють отримати доступ до нижньої структури даних, не копіюючи її. Окрім того, що є динамічним на відміну від створення списку, одним із найкорисніших їх використання є inтест. Скажіть, ви хочете перевірити, чи є значення у dict чи ні (чи це ключове, або значення).

Варіант перший - створити список клавіш за допомогою dict.keys(), це працює, але, очевидно, вимагає більше пам’яті. Якщо дикт дуже великий? Це було б марно.

За допомогою нього viewsможна повторити фактичну структуру даних без проміжного списку.

Давайте скористаємося прикладами. Я маю диктант з 1000 клавішами випадкових рядків і цифр, і kце ключ, який я хочу шукати

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?

Як бачите, ітераційний viewоб’єкт дає величезний приріст продуктивності, одночасно зменшуючи накладні витрати. Ви повинні використовувати їх, коли потрібно виконувати Setподібні операції.

Примітка : я працюю на Python 2.7


У python> = 3, я вважаю, .keys()повертає представлення за замовчуванням. Можливо, хочете подвійно перевірити тхо
Yolo Voe

1
Ти маєш рацію. Python 3+ робить велике використання об’єктів перегляду замість списків, це набагато ефективніше пам’яті
Chen A.

1
Ці результати в часі дуже показові, але перевірка того, чи kє одна з клавіш словника large_d, має бути зроблена k in large_dв Python, що, мабуть, є таким же швидким, як і використання перегляду (іншими словами, k in large_d.keys()не є Pythonic і його слід уникати - як є k in large_d.viewkeys()).
Ерік О Лебігот

Дякуємо, що надали вагомий корисний приклад. k in large_dнасправді значно швидше ніж k in large_d.viewkeys(), тому, ймовірно, цього слід уникати, але це має сенс k in large_d.viewvalues().
naught101
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.