Что такое объекты словарного представления?


159

В 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, с другой стороны, часто излишне создает новый список, цитируемый Rajendran T, который занимает память и время в количестве, пропорциональном длине списка). Чтобы продолжить аналогию с окном, если вы хотите увидеть пейзаж за стеной, вы просто делаете в нем отверстие (вы строите окно); Копирование ключей в список будет соответствовать рисованию копии ландшафта на вашей стене - копия занимает время, пространство и не обновляется сама.

Подводя итог, представления - это просто ... представления (окна) в вашем словаре, которые отображают содержимое словаря даже после его изменения. Они предлагают функции, которые отличаются от функций списков: список ключей содержит копию ключей словаря в данный момент времени, в то время как представление является динамическим и его получение намного быстрее, так как ему не нужно копировать какие-либо данные ( ключи или значения) для того, чтобы быть созданным.


6
+1. Хорошо, чем это отличается от прямого доступа к внутреннему списку ключей? Это быстрее, медленнее? Больше памяти эффективно? Ограничен? Если вы можете прочитать и отредактировать его, он чувствует себя точно так же, как ссылка на этот список.
E-Удовлетворительно

3
Спасибо. Дело в том, что представления - это ваш доступ к «внутреннему списку ключей» (обратите внимание, что этот «список ключей», однако, не является списком Python, но в точности является представлением). Представления более эффективны по памяти, чем списки ключей (или значений или элементов) Python 2, поскольку они ничего не копируют; они действительно похожи на «ссылку на список ключей» (обратите внимание, что «ссылка на список» на самом деле просто называется списком в Python, поскольку списки являются изменяемыми объектами). Также обратите внимание, что вы не можете напрямую редактировать представления: вместо этого вы по-прежнему редактируете словарь, и представления немедленно отражают ваши изменения.
Эрик О Лебиго

3
Хорошо, я пока не совсем понял, как это реализовать, но пока это лучший ответ.
E-Удовлетворительно

2
Спасибо. Действительно, этот ответ в основном о семантике представлений. У меня нет информации об их реализации в CPython, но я бы предположил, что представление - это в основном указатель на правильную структуру (и) (ключи и / или значения), и что структуры являются частью самого объекта словаря.
Эрик О Лебиго

5
Я думаю, что стоит указать, что пример кода в этом посте взят из python3, а не из того, что я получил в python2.7.
2012 года

21

Как вы упомянули, dict.items()возвращает копию списка словарных пар (ключ, значение), которые расточительны и dict.iteritems()возвращают итератор для пар словаря (ключ, значение).

Теперь возьмем следующий пример, чтобы увидеть разницу между интегратором dict и представлением 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

В то время как вид просто показывает вам, что в диктанте. Это не волнует, если это изменилось:

>>> 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. Представления «псевдо-подобны» в том смысле, что они не поддерживают индексацию, поэтому вы можете с ними проверять членство и выполнять итерацию по ним (поскольку ключи являются хэшируемыми и уникальными, представления ключей и элементов являются более " set-like "в том, что они не содержат дубликатов).
  2. Вы можете хранить их и использовать их несколько раз, как список версий.
  3. Поскольку они отражают базовый словарь, любое изменение в словаре изменит представление и почти наверняка изменит порядок итерации . Таким образом, в отличие от списка версий, они не являются "стабильными".
  4. Поскольку они отражают основной словарь, они почти наверняка являются небольшими прокси-объектами; Копирование ключей / значений / элементов потребовало бы, чтобы они каким-то образом смотрели исходный словарь и копировали его несколько раз, когда происходят изменения, что было бы абсурдной реализацией. Так что я ожидал бы очень небольшую нагрузку на память, но доступ был бы немного медленнее, чем непосредственно к словарю.

Поэтому я предполагаю, что ключевой вариант использования - это если вы держите словарь и многократно перебираете его ключи / элементы / значения с изменениями между ними. Вы можете просто использовать вместо этого представление, превращаясь for k, v in mydict.iteritems():в for k, v in myview:. Но если вы просто перебираете словарь один раз, я думаю, что iter-версии все еще предпочтительнее.


2
+1 за анализ "за" и "против" из немногих сведений, которые мы получили.
E-Удовлетворительно

Если я создаю итератор поверх представления, он все равно становится недействительным при каждом изменении словаря. Это та же проблема, что и с итератором над самим словарем (например iteritems()). Так в чем же смысл этих взглядов? Когда я счастлив иметь их?
Alfe

@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()возвращает представление по умолчанию. Может быть, хочу дважды проверить это
Йоло Во

1
Ты прав. Python 3+ интенсивно использует объекты представления вместо списков, это намного более эффективно использует память
Чен А.

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.