Подтвердить что ты не робот

Нежелательное поведение от dict.fromkeys

Я бы хотел инициализировать словарь наборов (в Python 2.6) с помощью dict.fromkeys, но результирующая структура ведет себя странно. Более конкретно:

>>>> x = {}.fromkeys(range(10), set([]))
>>>> x
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 6: set([]), 7: set([]), 8: set([]), 9: set([])}
>>>> x[5].add(3)
>>>> x
{0: set([3]), 1: set([3]), 2: set([3]), 3: set([3]), 4: set([3]), 5: set([3]), 6: set([3]), 7: set([3]), 8: set([3]), 9: set([3])}

Я, очевидно, не хочу добавлять 3 ко всем наборам, только к набору, соответствующему x[5]. Конечно, я могу избежать проблемы, инициализируя x без fromkeys, но я хотел бы понять, что мне здесь не хватает.

4b9b3361

Ответ 1

Второй аргумент dict.fromkeys - это просто значение. Вы создали словарь, который имеет тот же набор, что и значение для каждого ключа. Предположительно, вы понимаете, как это работает:

>>> a = set()
>>> b = a
>>> b.add(1)
>>> b
set([1])
>>> a
set([1])

вы видите такое же поведение; в вашем случае x[0], x[1], x[2] (и т.д.) все разные способы доступа к одному и тому же объекту set.

Это немного легче увидеть с объектами, строковое представление которых включает их адрес памяти, где вы можете видеть, что они идентичны:

>>> dict.fromkeys(range(2), object())
{0: <object object at 0x1001da080>,
 1: <object object at 0x1001da080>}

Ответ 2

Вы можете сделать это с помощью выражения генератора:

x = dict( (i,set()) for i in range(10) )

В Python 3 вы можете использовать понимание словаря:

x = { i : set() for i in range(10) }

В обоих случаях выражение set() оценивается для каждого элемента, а не оценивается один раз и копируется в каждый элемент.

Ответ 3

Из-за this из dictobject.c:

while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash))
{
            Py_INCREF(key);
            Py_INCREF(value);
            if (insertdict(mp, key, hash, value))
                return NULL;
}

value - это ваш "set ([])", он оценивается только один раз, а счетчик ссылок на их результат увеличивается и добавляется в словарь, он не оценивает его каждый раз, когда он добавляет в dict.

Ответ 4

Причина его работы в том, что set([]) создает объект (заданный объект). Затем Fromkeys использует этот конкретный объект для создания всех его записей в словаре. Рассмотрим:

>>> x
{0: set([]), 1: set([]), 2: set([]), 3: set([]), 4: set([]), 5: set([]), 
6: set([]), 7: set([]), 8: set([]), 9: set([])}
>>> x[0] is x[1]
True

Все наборы одинаковы!

Ответ 5


#To do what you want:

import copy
s = set([])
x = {}
for n in range(0,5):
  x[n] = copy.deepcopy(s)
x[2].add(3)
print x

#Printing
#{0: set([]), 1: set([]), 2: set([3]), 3: set([]), 4: set([])}