In Python, an object field contains a reference to an object. Therefore, when you assign a new list in your example, you change the object referenced by the field, not its contents. Before changing the listofvalues property for an object that references the same list, but after affectation, they refer to two different lists.
This is equivalent to the following code:
>>> 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]
If you want to change the contents of the list, not the link, you need to use slicing. I.e:
>>> a = [4, 5, 3] >>> b = a >>> b[:] = [6, 7] >>> b [6, 7] >>> a [6, 7]
NOTE. The following is based on my understanding of Python 2.6's internal components. This, therefore, is implementation-specific, however the mental model that it gives you is probably pretty close to how the language rule is written and should work with any implementation.
In Python, objects are always referenced (as in Java, not C ++). However, the variable name or attribute name can be implemented as bindings in the dictionary as such in CPython (except for the optimization of local variables, the presence of __slots__ or pseudo-attributes opened through __getattr__ and friends).
In Python, each object is a private dictionary that matches each attribute name with a value. And the interpreter has two private dictionaries containing a comparison of local and global variables with their meaning. When you change the value of a variable or attribute of an object, you simply change the binding in the corresponding dictionary.
So, in your example, you have the same behavior as in the following code:
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.