Как мне инициализировать словарь пустых списков в Python?
Моя попытка программно создать словарь списков не позволяет мне обращаться к ключам словаря по отдельности. Всякий раз, когда я создаю словарь списков и пытаюсь добавить к одному ключу, все они обновляются. Вот очень простой тестовый пример:
data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data
Фактический результат: {0: ['hello'], 1: ['hello']}
Ожидаемый результат: {0: [], 1: ['hello']}
Вот что работает
data = {0:[],1:[]}
data[1].append('hello')
print data
Фактический и ожидаемый результат: {0: [], 1: ['hello']}
Почему fromkeys
метод работает не так, как ожидалось?
Переведено автоматически
Ответ 1
Когда []
передается в качестве второго аргумента в dict.fromkeys()
, все значения в результирующем dict
будут одним и тем же list
объектом.
В Python 2.7 или выше вместо этого используйте понимание dict:
data = {k: [] for k in range(2)}
В более ранних версиях Python нет понимания dict, но понимание списка может быть передано конструктору dict
вместо этого:
data = dict([(k, []) for k in range(2)])
В 2.4-2.6 также возможно передать выражение генератора в dict
, а окружающие круглые скобки можно убрать:
data = dict((k, []) for k in range(2))
Ответ 2
Попробуйте использовать defaultdict вместо этого:
from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')
Таким образом, ключи не нужно инициализировать пустыми списками заранее. Вместо этого defaultdict()
объект вызывает предоставленную ему заводскую функцию каждый раз, когда осуществляется доступ к ключу, который еще не существует. Итак, в этом примере попытка получить доступ к data[1]
срабатывает data[1] = list()
внутренне, присваивая этому ключу новый пустой список в качестве его значения.
Исходный код с .fromkeys
общим одним (изменяемым) списком. Аналогично,
alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print(data)
выводил бы {0: [1, 2], 1: [1, 2]}
. Это вызвано в dict.fromkeys()
документации:
Все значения относятся только к одному экземпляру, поэтому обычно не имеет смысла, чтобы value был изменяемым объектом, таким как пустой список.
Другой вариант - использовать dict.setdefault()
метод, который извлекает значение для ключа после первой проверки его существования и установки значения по умолчанию, если оно не существует. .append
затем можно вызвать результат:
data = {}
data.setdefault(1, []).append('hello')
Наконец, чтобы создать словарь из списка известных ключей и заданного списка "шаблонов" (где каждое значение должно начинаться с одних и тех же элементов, но представлять собой отдельный список), используйте понимание словаря и скопируйте исходный список:
alist = [1]
data = {key: alist[:] for key in range(2)}
Здесь alist[:]
создается поверхностная копия alist
, и это делается отдельно для каждого значения. Смотрите Как мне клонировать список, чтобы он неожиданно не изменился после назначения? дополнительные методы копирования списка.
Ответ 3
Вы могли бы использовать понимание dict:
>>> keys = ['a','b','c']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
{'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
Ответ 4
Этот ответ здесь , чтобы объяснить это поведение всем, кто сбит с толку результатами, которые они получают при попытке создать экземпляр dict
with fromkeys()
с изменяемым значением по умолчанию в этом dict
.
Рассмотрим:
#Python 3.4.3 (default, Nov 17 2016, 01:08:31)
# start by validating that different variables pointing to an
# empty mutable are indeed different references.
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968
таким образом, любое изменение на l1
не повлияет l2
и наоборот.
это было бы верно для любого изменяемого параметра на данный момент, включая dict
.
# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
>>> dict1
{'c': [], 'b': [], 'a': []}
это может быть удобной функцией.
здесь мы присваиваем каждому ключу значение по умолчанию, которое также является пустым списком.
# the dict has its own id.
>>> id(dict1)
140150327601160
# but look at the ids of the values.
>>> id(dict1['a'])
140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328
Действительно, все они используют один и тот же ref!
Изменение одного - это изменение всех, поскольку фактически это один и тот же объект!
>>> dict1['a'].append('apples')
>>> dict1
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
>>> id(dict1['a'])
>>> 140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328
для многих это было не то, что задумывалось!
Теперь давайте попробуем сделать явную копию списка, используемого в качестве значения по умолчанию.
>>> empty_list = []
>>> id(empty_list)
140150324169864
а теперь создайте dict с копией empty_list
.
>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
140150323831432
>>> id(dict2['a'])
140150327184328
>>> id(dict2['b'])
140150327184328
>>> id(dict2['c'])
140150327184328
>>> dict2['a'].append('apples')
>>> dict2
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
По-прежнему никакой радости!
Я слышу, как кто-то кричит, это потому, что я использовал пустой список!
>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
{'c': [0], 'b': [0], 'a': [0]}
>>> dict3['a'].append('apples')
>>> dict3
{'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}
Поведение по умолчанию fromkeys()
заключается в присвоении None
значению.
>>> dict4 = dict.fromkeys(['a', 'b', 'c'])
>>> dict4
{'c': None, 'b': None, 'a': None}
>>> id(dict4['a'])
9901984
>>> id(dict4['b'])
9901984
>>> id(dict4['c'])
9901984
Действительно, все значения одинаковы (и единственные!) None
.
Теперь давайте выполним итерацию одним из множества способов через dict
и изменим значение.
>>> for k, _ in dict4.items():
... dict4[k] = []
>>> dict4
{'c': [], 'b': [], 'a': []}
Хм. Выглядит так же, как и раньше!
>>> id(dict4['a'])
140150318876488
>>> id(dict4['b'])
140150324122824
>>> id(dict4['c'])
140150294277576
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}
Но они действительно разные []
s, что и было в данном случае предполагаемым результатом.