2010-03-13 4 views
30

Я бы ожидал, что следующий код просто инициализирует словари dict_a, dict_b и dict_c. Но швы иметь копию через эффект:Python Copy Through Assignment?

dict_a = dict_b = dict_c = {} 
dict_c['hello'] = 'goodbye' 

print dict_a 
print dict_b 
print dict_c 

Как вы можете видеть результат выглядит следующим образом:

{'hello': 'goodbye'} 
{'hello': 'goodbye'} 
{'hello': 'goodbye'} 

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

{} 
{} 
{'hello': 'goodbye'} 
+0

Нет учебник, я просто применяя понятия я думал, работал в таких языках, как Java , –

ответ

30

Это потому, что в Python переменные (имена) являются просто ссылками на отдельные объекты. Когда вы назначаете dict_a = dict_b, вы действительно копируете адрес памяти (или указатель, если хотите) от dict_b до dict_a. Существует еще один экземпляр этого словаря.

Чтобы получить желаемое поведение, используйте метод dict.copy или используйте copy.deepcopy, если ваш dict может иметь вложенные диктофоны или другие вложенные объекты.

>>> a = {1:2} 
>>> b = a.copy() 
>>> b 
{1: 2} 
>>> b[3] = 4 
>>> a 
{1: 2} 
>>> b 
{1: 2, 3: 4} 
>>> 
+0

Итак, словари - это объекты, будет ли это так, например, с целочисленной переменной или тип переменной не повлияет на то, что он является объектом? –

+0

Целые числа неизменны, поэтому я не уверен, как вы хотели бы воспроизвести это поведение. В любом случае литеральные значения для некоторых типов интернированы (т. Е. Есть только один экземпляр каждого). Вы можете проверить, что две целые переменные, содержащие одно и то же значение, фактически указывают на один и тот же экземпляр целого числа с помощью функции id(). – danben

+1

В любом случае все в Python является объектом. Чтобы увидеть функции, принадлежащие целочисленному объекту 1, вы можете выполнить 'dir (1)'. – danben

1

Ваше первое назначение присваивает тот же словарь объект переменным dict_a, dict_b и dict_c. Это эквивалентно dict_c = {}; dict_b = dict_c; dict_a = dict_c.

2

Как ранее говорил данбэн, вы просто копируете один и тот же dict в 3 переменные, так что каждый из них обращается к одному и тому же объекту.

Чтобы получить поведение, которое вы хотите, вы должны создании экземпляра другого Dict в каждой переменной:

>>> dict_a, dict_b, dict_c = {}, {}, {} 
>>> dict_c['hello'] = 'goodbye' 
>>> print dict_a 
{} 
>>> print dict_b 
{} 
>>> print dict_c 
{'hello': 'goodbye'} 
>>> 
+0

Спасибо, это было то, что я собирался делать, когда наткнулся на эту тему. Хотя это не ответ на вопрос, спасибо очень. –

8

Даже если

>>> dict_a, dict_b, dict_c = {}, {}, {} 

правильный путь в большинстве случаев, когда они получают более 3 выглядит странно

Представьте

>>> a, b, c, d, e, f = {}, {}, {}, {}, {}, {} 

В тех случаях, когда я хочу инициализировать более 3 вещи, я использую

>>> a, b, c, d, e, f, = [dict() for x in range(6)] 

Внимание: Не используйте [{} for x in range(6)]

+3

«Внимание: не используйте [{} для x в диапазоне (6)]« Почему бы и нет? выражение элемента выполняется один раз для каждой итерации, и каждый раз, когда выполняется литерал '{}', он возвращает новый dict. На самом деле это должно быть предпочтительнее, чем 'dict()', поскольку он избегает вызова функции. – SingleNegationElimination

+0

@TokenMacGuy, да и нет. Теперь, когда я перечитываю то, что я написал, я вижу вашу мысль. '{}' получает инициализацию каждый раз. Тем не менее '{}' - это просто синтаксический сахар для 'dict()'. Вы ничего не спасаете, используя прежний. –

+5

Джеффри Хосе: Сомневаюсь, Томас? 'import dis; print dis.dis (lambda: dict()); print dis.dis (lambda: {}) ' – SingleNegationElimination