I have a friend struggling with some code wight now, so I’m copy-pasting an interactive session with some object reference basics that I hope will clarify some things 😉

There’s also a pastie for this in http://paste.lisp.org/+2E0W for easier reading.

>>> # First, lets make a Class to help with tests
>>> class Coco(object):
	def __init__(self, value=0):
		self.value = value

>>> # Lets make a list of instances, the dumb way
>>> a = [Coco()] * 4
>>> a
[<__main__.Coco object at 0xb64c9cac>, <__main__.Coco object at 0xb64c9cac>,
 <__main__.Coco object at 0xb64c9cac>, <__main__.Coco object at 0xb64c9cac>]

>>> # See that the addresses are the same? Lets look values
>>> [i.value for i in a]
[0, 0, 0, 0]

>>> # Now, let's try to change one of them
>>> a[0].value = 1

>>> # We now expect [1, 0, 0, 0], right?
>>> [i.value for i in a]
[1, 1, 1, 1]

>>> # NOPE! All of them changed because it's THE SAME OBJECT
>>> 

>>> # Let's do it again the smart way
>>> a = []
>>> for i in range(4):
	a.append(Coco())

>>> a
[<__main__.Coco object at 0xb64b906c>, <__main__.Coco object at 0xb64d128c>,
 <__main__.Coco object at 0xb64d1c2c>, <__main__.Coco object at 0xb64d1c0c>]

>>> # See that the addresses are different?
>>> [i.value for i in a]
[0, 0, 0, 0]

>>> a[0].value = 1
>>> [i.value for i in a]
[1, 0, 0, 0]

>>> # Pretty cool huh?
>>> 

>>> # Lets also put the list in variable b
>>> b = a
>>> [i.value for i in a]
[1, 0, 0, 0]

>>> [i.value for i in b]
[1, 0, 0, 0]

>>> # And change a value in b
>>> b[0].value = 0
>>> [i.value for i in b]
[0, 0, 0, 0]
>>> [i.value for i in a]
[0, 0, 0, 0]
>>> # a and b are the same object. A no brainer!
>>> 

>>> # Now let's copy once with copy() and once with deepcopy()
>>> from copy import copy, deepcopy
>>> a_copy = copy(a) # You can also do =a[:] for lists and =a.copy() for dicts
>>> a_deepcopy = deepcopy(a)
>>> a
[<__main__.Coco object at 0xb64b906c>, <__main__.Coco object at 0xb64d128c>,
 <__main__.Coco object at 0xb64d1c2c>, <__main__.Coco object at 0xb64d1c0c>]

>>> a_copy
[<__main__.Coco object at 0xb64b906c>, <__main__.Coco object at 0xb64d128c>,
 <__main__.Coco object at 0xb64d1c2c>, <__main__.Coco object at 0xb64d1c0c>]

>>> a_deepcopy
[<__main__.Coco object at 0xb64d1a4c>, <__main__.Coco object at 0xb64d1a2c>,
 <__main__.Coco object at 0xb64d1a0c>, <__main__.Coco object at 0xb64d19ec>]

>>> a_copy[0].value = 4
>>> a_deepcopy[1].value = 6
>>> # Can you foretell what will happen now?
>>> [i.value for i in a_copy]
[4, 0, 0, 0]

>>> [i.value for i in a_deepcopy]
[0, 6, 0, 0]

>>> [i.value for i in a]
[4, 0, 0, 0]

>>> # While a and a_copy are not the same, their contents are shared!
>>> # Here's how to try when in doubt
>>> a is b
True

>>> a is a_copy
False

>>> a[0] is a_copy[0]
True

>>> a[0] is a_deepcopy[0]
False

>>>
>>> # PS. If you make a list the dumb way
>>> a = [Coco()] * 4

>>> # ..you'll have a list of the same objects even after a deepcopy
>>> k = [Coco()] * 4
>>> k_deepcopy = deepcopy(k)
>>> k_deepcopy[0].value = 1
>>> [i.value for i in k_deepcopy]
[1, 1, 1, 1]

>>> k_deepcopy[0] is k_deepcopy[3]
True

>>> # ..although, not the same ones as the originals
>>> [i.value for i in k]
[0, 0, 0, 0]

>>> k_deepcopy[0] is k[0]
False

>>>
>>> # Hope that's helpful ;)
>>>

Nick

Advertisements