В Python поле объекта сохраняет ссылку на объект. Поэтому, когда вы назначаете новый список в своем примере, вы изменяете объект, на который ссылается поле, а не его содержимое. До воздействия свойство listofvalues
обоих объектов ссылается на один и тот же список, но после воздействия они ссылаются на два разных списка.
Это эквивалентно следующему коду:
>>> a = [4, 5]
>>> b = a
>>> b.append(3)
>>> b
[4, 5, 3]
>>> a
[4, 5, 3]
>>> b = [6, 7]
>>> b
[6, 7]
>>> a
[4, 5, 3]
Если вы хотите изменить содержимое списка, а не ссылку, вам нужно использовать нарезку. То есть:
>>> a = [4, 5, 3]
>>> b = a
>>> b[:] = [6, 7]
>>> b
[6, 7]
>>> a
[6, 7]
ПРИМЕЧАНИЕ: следующее основано на моем понимании внутренних элементов Python 2.6. Таким образом, он действительно специфичен для реализации, однако ментальная модель, которую он вам дает, вероятно, довольно близка к тому, как написано правило языка, и должна работать с любой реализацией.
В Python доступ к объектам всегда осуществляется через ссылки (как в Java, а не в C ++). Однако имя переменной или имени атрибута может быть, однако, как привязка в словаре и реализовано как таковое в CPython (за исключением оптимизации локальной переменной, наличия __slots__
или псевдоатрибутов, предоставляемых через __getattr__
и друзья).
В Python каждый объект как частный словарь отображает каждое имя его атрибута в значение. И у интерпретатора есть два частных словаря, в которых сопоставление между именем локальной и глобальной переменной и их значением. Когда вы изменяете значение переменной или атрибута объекта, вы просто изменяете привязку в соответствующем словаре.
Итак, в вашем примере вы ведете себя так же, как в следующем коде:
def understand_copy():
return {'listofvalues': [4, 5]}
def deepcopy(obj):
if instance(obj, dict):
copy = {}
for key, value in obj.iteritems():
copy[key] = deepcopy(value) # Note the recursion
return copy
if instance(obj, list):
copy = []
for value in obj:
copy.append(deepcopy(value)) # Note the recursion
return copy
return obj
def copy(obj):
if instance(obj, dict):
copy = {}
for key, value in obj.iteritems():
copy[key] = value # No recursion this time, the copy is shallow
return copy
if instance(obj, list):
copy = []
for value in obj:
copy.append(value) # No recursion this time, the copy is shallow
return copy
return obj
globals = {}
globals['ins'] = understand_copy()
globals['new'] = copy(global['ins'])
# Now globals['ins']['listofvalues']
# and globals['new']['listofvalues']
# reference the same object!
globals['ins']['listofvalues'].__setitem__(0, 3)
globals['ins']['listofvalues'].append(5)
# We are calling function on one object,
# but not changing a binding, so the two
# 'listofvalues' attribute still refers
# to the same object.
globals['ins']['listofvalues'] = [10, 11]
# Now we changed the binding of the name
# in the dictionary 'ins' so now the two
# objects attributes points to different
# lists.