always the same object (2)

U

Uwe Mayer

Hi,

sorry for the lack of source code.
Here again:

I use struct.unpack() to unpack data from a binary file and pass the
returned tuple as parameter to __init__ of a class that's supposed to
handle the data:

class DataWrapper():
data = { }
def __init__(self, arg): #arg will contain a tuple
data['var1'], data['var2'] = arg


result = [ ]
while not <end-of-file f>:
data = struc.unpack("4s4s", f.read(8))
record = DataWrapper( data ) # pass tuple from unpack
result.append( record )

Then "result" contains a list with different objects, but the strings
in data['var1'] and data['var2'] are all the same.

Any ideas how to avoid this?

Thanks again
Ciao
Uwe
 
U

Uwe Mayer

Uwe Mayer wrote:

in said:
class DataWrapper():
data = { }
def __init__(self, arg): #arg will contain a tuple
data['var1'], data['var2'] = arg

the "data" variable is a class variable and shared by all DataWrapper,
therfore:
result = [ ]
while not <end-of-file f>:
data = struc.unpack("4s4s", f.read(8))
record = DataWrapper( data ) # pass tuple from unpack
result.append( record )

all "record"s in "result" share the same instance and thus all the data gets
overwritten. :)

Moving "data" into __init__() in DataWrapper does the trick.

Thanks again
Ciao
Uwe
 
D

David Bolen

(...)
I use struct.unpack() to unpack data from a binary file and pass the
returned tuple as parameter to __init__ of a class that's supposed to
handle the data:

class DataWrapper():
data = { }
def __init__(self, arg): #arg will contain a tuple
data['var1'], data['var2'] = arg

Is this actual code? You should get a SyntaxError on your use of ()
in the class line, and a NameError on your use of data[] since there
is no local data name within the __init__ function. I'm assuming your
real code actually does self.data, and skips the () on the class
statement, but in the future, it's really best if you can post actual
operating code when discussing a problem.
result = [ ]
while not <end-of-file f>:
data = struc.unpack("4s4s", f.read(8))
record = DataWrapper( data ) # pass tuple from unpack
result.append( record )

Then "result" contains a list with different objects, but the strings
in data['var1'] and data['var2'] are all the same.

Any ideas how to avoid this?

Yes, don't make data a class-level object. By doing this you have a
single instance of the dictionary that data is pointing to, which is
shared among all of your DataWrapper instances. Since that class
object is mutable, all of your changes in each instances __init__
affect that single object. Instead, move the creation of the instance
name (and object) to the __init__ in your instance.

This can be a subtle point sometimes, because you won't run into this
problem with class level immutable objects (such as ints), because any
assignment to the same name in an instance becomes a rebinding
operation, thus making the name an instance variable at that point in
time. This can be very convenient for implementing class level
defaults that take up no space in each instance, but doesn't work the
same as mutable types.

Note that if you really mean to do it, using a mutable type at class
level can be a very useful construct, since it permits sharing of
information among all instances of a class. But you have to be
expecting it :)

For example, the immutable case:
... var = 10
... def set_var(self, value):
... print 'Var ID:', id(self.var)
... self.var = value
... print 'Var ID:', id(self.var)
... Var ID: 7649236
Var ID: 7649224 10 5

Foo.var always references the same object, but once an assignment has
been made within the instance, it no longer points to the same object
as initialized at class level. Thus, each instance references
independent objects with their own values.

Contrast this with the mutable case:
... var = []
... def set_var(self, value):
... print 'Var ID:', id(self.var)
... self.var.append(value)
... print 'Var ID:', id(self.var)
...
>>> x = Foo()
>>> y = Foo()
>>> print id(Foo.var), id(x.var), id(y.var) 8042256 8042256 8042256
>>> print Foo.var, x.var, y.var [] [] []
>>> x.set_var(10)
Var ID: 8042256
Var ID: 8042256 Var ID: 8042256
Var ID: 8042256 [10, 5] [10, 5] [10, 5]

I made the class object a list and appended values to it just to
highlight how calls to multiple instances were affecting the same
object, but the principle is the same with a dictionary or any other
mutable object. Since the changes in set_var are made by mutating the
existing object (versus rebinding the same name to a new object), all
the instances share the single Foo.var list.

So you could use this construct if you really wanted an object that
was seen by all instances and updated by any instance. But if you
just wanted an empty list (or dictionary) independently in each
instance, you'd want to do something like:
... def __init__(self):
... self.var = []
... def set_var(self, value):
... print 'Var ID:', id(self.var)
... self.var.append(value)
... print 'Var ID:', id(self.var)
... Traceback (most recent call last):
Var ID: 7756800
Var ID: 7756800 Var ID: 8053200
Var ID: 8053200 [10] [5]

-- David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top