3

I broke down my problem to the following example program

xy8_block = [
    {'pulse': {}},
]

class Dummy:
    def __init__(self, block=list(xy8_block)):
        self._block = block


dumdum = Dummy()
dumdum._block[0]['pulse']['test'] = 0
print(xy8_block)

If I run the program, the variable xy8_block is changed, although both variables dumdum._block and xy8_block have both a different memory address.

How can I avoid this problem without directly initialising the class with the value.

Thanks in advance.

Januka samaranyake
  • 2,385
  • 1
  • 28
  • 50
SourBitter
  • 157
  • 2
  • 11
  • @lucasnadalutti is correct, you can prove this by looking at the ids of the dictionary : ``print id(xy8_block[0]) `` and ``print id(dumdum._block[0])`` – Richard Green Nov 24 '16 at 17:50
  • Possible duplicate of ["Least Astonishment" and the Mutable Default Argument](http://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument) – wwii Nov 24 '16 at 18:56

2 Answers2

4

Instead of:

def __init__(self, block=list(xy8_block)):

Do:

from copy import deepcopy
def __init__(self, block=deepcopy(xy8_block)):

When you do list(my_list), you do a shallow copy of your list, which means its elements are still copied by reference.

In other words, as you correctly mentioned, xy8_block and dumdum._block do have different memory addresses. However, if you check memory addresses for xy8_block[0] and dumdum._block[0], you will see that they are the same.

By using deepcopy, you copy the list and its elements' values, not their references.

EDIT

As wisely noted by @FMc, this will still make all instances' _block attributes to point to the same object, since the list that results from deepcopy is created in the method's definition, not in its execution. So here's my suggestion:

from copy import deepcopy

class Dummy:
    def __init__(self, block=None):
        self._block = block or deepcopy(xy8_block)
lucasnadalutti
  • 5,818
  • 1
  • 28
  • 48
  • This won't work as intended. Every instance of `Dummy` will hold a reference to the same deep copy. Default arg values are defined once, at class definition time. That's one reason why you typically want to avoid mutable values as default values. – FMc Nov 24 '16 at 17:51
  • Although it does work for the specific given example, you're right about that. I will elaborate, thanks a lot. – lucasnadalutti Nov 24 '16 at 17:52
2

Get a fresh block each time:

def new_xy8_block():
    return [{'pulse': {}}]

class Dummy:
    def __init__(self, block=None):
        self._block = block or new_xy8_block()

ds = [Dummy() for _ in range(5)]
ds[0]._block[0]['pulse']['test'] = 0

for d in ds:
    print d._block
FMc
  • 41,963
  • 13
  • 79
  • 132