always the same object (2)

Discussion in 'Python' started by Uwe Mayer, Jan 21, 2004.

  1. Uwe Mayer

    Uwe Mayer Guest

    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
     
    Uwe Mayer, Jan 21, 2004
    #1
    1. Advertising

  2. Uwe Mayer

    Uwe Mayer Guest

    Uwe Mayer wrote:

    Thanks for all the responses. As John Roth <> wrote
    in <>:

    > 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
     
    Uwe Mayer, Jan 21, 2004
    #2
    1. Advertising

  3. Uwe Mayer

    David Bolen Guest

    Uwe Mayer <> writes:

    (...)
    > 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:

    >>> class Foo:

    ... var = 10
    ... def set_var(self, value):
    ... print 'Var ID:', id(self.var)
    ... self.var = value
    ... print 'Var ID:', id(self.var)
    ...
    >>> x = Foo()
    >>> print id(Foo.var), id(x.var)

    7649236 7649236
    >>> x.set_var(5)

    Var ID: 7649236
    Var ID: 7649224
    >>> print id(Foo.var), id(x.var)

    7649236 7649224
    >>> print Foo.var, x.var

    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:

    >>> class Foo:

    ... 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
    >>> y.set_var(5)

    Var ID: 8042256
    Var ID: 8042256
    >>> print id(Foo.var), id(x.var), id(y.var)

    8042256 8042256 8042256
    >>> print Foo.var, x.var, y.var

    [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:

    >>> class Foo:

    ... 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)
    ...
    >>> x = Foo()
    >>> y = Foo()
    >>> print id(Foo.var)

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    AttributeError: class Foo has no attribute 'var'
    >>> print id(x.var), id(y.var)

    7756800 8053200
    >>> x.set_var(10)

    Var ID: 7756800
    Var ID: 7756800
    >>> y.set_var(5)

    Var ID: 8053200
    Var ID: 8053200
    >>> print id(x.var), id(y.var)

    7756800 8053200
    >>> print x.var, y.var

    [10] [5]

    -- David
     
    David Bolen, Jan 21, 2004
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Haydnw
    Replies:
    5
    Views:
    470
    =?Utf-8?B?amdyYW50?=
    Sep 25, 2004
  2. ravi mannan
    Replies:
    2
    Views:
    466
    Christophe Vanfleteren
    Nov 23, 2003
  3. Deryck
    Replies:
    4
    Views:
    519
    derek giroulle
    Jun 22, 2004
  4. Uwe Mayer

    always the same object

    Uwe Mayer, Jan 20, 2004, in forum: Python
    Replies:
    3
    Views:
    379
    John Roth
    Jan 21, 2004
  5. Mug
    Replies:
    4
    Views:
    302
Loading...

Share This Page