3

I am reading a binary file into a list of class instances. I have a loop that reads data from the file into an instance. When the instance is filled, I append the instance to a list and start reading again.

This works fine except that one of the elements of the instance is a Rect (i.e. rectangle), which is a user-defined type. Even with deepcopy, the attributes are overwritten.

There are work-arounds, like not having Rect be a user-defined type. However, I can see that this is a situation that I will encounter a lot and was hoping there was a straightforward solution that allows me to read nested types in a loop.

Here is some code:

class Rect:
    def __init__(self):
        self.L = 0

class groundtruthfile:
    def __init__(self):
        self.rect = Rect
        self.ht = int
        self.wt = int
        self.text = ''
        ...

data = []
g = groundtruthfile()
f = open("datafile.dtf", "rb")
length = unpack('i', f.read(4))
for i in range(1,length[0]+1): #length is a tuple
    g.rect.L = unpack('i',f.read(4))[0]
    ...
    data.append(copy.deepcopy(g))

The results of this are exactly what I want, except that all of the data(i).rect.L are the value of the last data read.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
j t
  • 33
  • 4
  • 2
    You're *explicitly* modifying the same `Rect` instance (actually the *class*, you don't instantiate an instance in `groundtruthfile.__init__`, which should be named `GroundTruthFile`) - why would you expect different behaviour?! – jonrsharpe Oct 05 '15 at 16:03
  • 1
    @DSM yes, just spotted that - seems a bit rich to call Python unintuitive here! – jonrsharpe Oct 05 '15 at 16:04

1 Answers1

4

You have two problems here:

  1. The rect attribute of a groundtruthfile instance (I'll just put this here...) is the Rect class itself, not an instance of that class - you should be doing:

    self.rect = Rect()  # note parentheses
    

    to create an instance, instead (similarly e.g. self.ht = int sets that attribute to the integer class, not an instance); and

  2. The line:

    g.rect.L = unpack('i',f.read(4))[0]
    

    explicitly modifies the attribute of the same groundtruthfile instance you've been using all along. You should move the line:

    g = groundtruthfile()
    

    inside the loop, so that you create a separate instance each time, rather than trying to create copies.

This is just a minimal fix - it would make sense to actually provide arguments to the various __init__ methods, for example, such that you can create instances in a more intuitive way.


Also, if you're not actually using i in the loop:

for _ in range(length[0]):

is neater than:

for i in range(1,length[0]+1):
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Thanks for the reply. – j t Oct 07 '15 at 06:15
  • I have enough to make the program work and that is enough for right now. I still don't get the functioning of deepcopy though: I thought it explicitly created a new instance, sort of like 'copy value' in excel. I was trying to keep the same reference to a memory location each time and just change the value of that memory location in each loop (which I think is what happens). I thought the append(deepcopy) statement was then creating a new variable, data [i] (even though i isn't used explicitly) with each append(deepcopy()) statement which is filled with a value only. So what does deepcopy do? – j t Oct 07 '15 at 06:31
  • @jt `deepcopy` only applies to *instance attributes*, not *class attributes* - see e.g. http://stackoverflow.com/q/18364284/3001761 – jonrsharpe Oct 07 '15 at 09:07
  • OK, thanks. I will play with it and see if I can get a better handle on it. – j t Oct 08 '15 at 09:18