Як ініціалізувати словник порожніх списків у Python?


88

Моя спроба програмно створити словник списків не дозволяє мені окремо звертатися до ключів словника. Кожного разу, коли я створюю словник списків і намагаюся додати одну клавішу, всі вони оновлюються. Ось дуже простий тестовий приклад:

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метод не працює належним чином?

Відповіді:


111

Передача []в якості другого аргументу для dict.fromkeys()дає досить марний результат - усі значення у словнику будуть однаковим об’єктом списку.

У Python 2.7 або новіших версіях замість цього ви можете використовувати розуміння дицитон:

data = {k: [] for k in range(2)}

У попередніх версіях Python ви можете використовувати

data = dict((k, []) for k in range(2))

3
Ну, це досить неінтуїтивна поведінка, будь-яка ідея щодо того, чому один і той же об’єкт використовується для всіх ключів?
Бар

2
@Bar Оскільки в семантиці мови Python функція не може зробити нічого іншого. Ви передаєте один об’єкт, який буде використовуватися як значення для всіх ключів, так що цей об’єкт використовується для всіх ключів. Для fromkeys()методу було б краще прийняти заводську функцію, щоб ми могли перейти listяк функція, і цей функціонал буде викликатися один раз для кожного створеного ключа, але це не фактичний API dict.fromkeys().
Свен Манах

3
Це зовсім не інтуїтивно. Це знайшло мені годину, щоб знайти. Дякуємо
Астрід

1
Те саме відбувається, якщо ви передаєте dict () як другий аргумент. Дуже незрозуміла поведінка.
Орлі,

@Orly Це тому, що спочатку створюється один порожній словник, а потім посилання на нього передається всім ініціалізаціям.
Dr_Zaszuś

83

Замість цього використовуйте defaultdict :

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

Таким чином, вам не потрібно попередньо ініціалізувати всі клавіші, які ви хочете використовувати, у списках.

У вашому прикладі відбувається те, що ви використовуєте один (змінний) список:

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

виводить {0: [1, 2], 1: [1, 2]}.


2
У моєму випадку мені потрібно заздалегідь ініціалізувати всі клавіші, щоб решта логіки програми могла працювати належним чином, але в іншому випадку це було б гарним рішенням. Дякую.
Martin Burch

Я здогадуюсь, чого не вистачає у цій відповіді, це те, що це рішення працює, на відміну від OP, оскільки listтут не порожній список, а тип (або ви можете бачити його як конструктор, що викликається, я думаю). Отже, кожного разу, коли передається відсутній ключ, замість повторного використання того самого створюється новий список.
Dr_Zaszuś

8

Ви заповнюєте свої словники посиланнями на один список, тому, коли ви його оновлюєте, оновлення відображається у всіх посиланнях. Натомість спробуйте зрозуміти словник. Див. Створення словника з розумінням списку на Python

d = {k : v for k in blah blah blah}

чудова пропозиція щодо ініціалізації значень словника ... дякую, cobie! Я розширив ваш приклад, щоб скинути значення в існуючому словнику, d. Я виконав це наступним чином: d = {k: 0 для k in d}
Джон

Що vв цій відповіді?
Dr_Zaszuś

-2

Ви можете використовувати це:

data[:1] = ['hello']

2
Можливо, корисно для ОП пояснити, чому це працює. Оригінальне запитання опублікувало, чому це не працює належним чином.
william.taylor.09

@ william.taylor.09, певно, чому це працює, чи не так?
Коннер Дассен,

OP запитує ("запитував") "Чому метод fromkeys не працює належним чином?"
william.taylor.09
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.