sharing dictionaries amongst class instances

Discussion in 'Python' started by Kerry Neilson, Nov 9, 2003.

  1. Hi,
    Really hung up on this one. I'm trying to get all the fields of a
    dictionary to be unique for each class:

    class A {
    my_dict = []
    dict_entry = { 'key1':0, 'key2':0 }

    __init__(self):
    for x in range(10):
    tmp = copy.deepcopy(self.dict_entry)
    tmp['key1'] = x
    self.my_dict.append(tmp)
    }

    in a driver, I have

    inst0, inst1 = A.A(), A.A()
    inst0.my_dict[1]['key2'] = "ted"
    inst1.my_dict[5]['key2'] = "steve"
    inst0.display()
    inst1.display()

    printing them out shows that both objects have ted and steve in their
    dictionaries. I have done this very thing outside of a class, and it works
    wonderfully. All other attributes in the class are able to remain unique.
    ie, inst0.data = 5 and inst1.data = 8 works fine.
    I believe I could set the dictionaries up beforehand, but they will be
    substatially different sizes, and it certainly wouldn't be proper.

    Any ideas? Thanks very much for any insight.
    Kerry Neilson, Nov 9, 2003
    #1
    1. Advertising

  2. Kerry Neilson

    Peter Otten Guest

    Kerry Neilson wrote:

    > Hi,
    > Really hung up on this one. I'm trying to get all the fields of a
    > dictionary to be unique for each class:
    >
    > class A {
    > my_dict = []
    > dict_entry = { 'key1':0, 'key2':0 }
    >
    > __init__(self):
    > for x in range(10):
    > tmp = copy.deepcopy(self.dict_entry)
    > tmp['key1'] = x
    > self.my_dict.append(tmp)
    > }


    class A { ... } is *not* Python. Are you converting from Java :) ?
    From what follows, I conclude that you want the dicitonary to be unique for
    each *instance* (if you want it unique for the class, you are already
    there). Unlike Java, you must put it in the constructor then (untested):

    class A:
    def __init__(self):
    self.my_dict = {"key1": 0, "key2": 0}

    Dictionaries are the {} thingies, [] indicate list literals. Deepcopy should
    be buried very deep in the documentation, where no newbie will ever find
    it...
    Seriously, take the time to read the excellent tutorial that comes with
    Python, and only then try to clean up this mess.

    Peter
    Peter Otten, Nov 9, 2003
    #2
    1. Advertising

  3. Kerry Neilson

    Andy Jewell Guest

    On Sunday 09 Nov 2003 8:22 pm, Kerry Neilson wrote:
    > Hi,
    > Really hung up on this one. I'm trying to get all the fields of a
    > dictionary to be unique for each class:
    >
    > class A {
    > my_dict = []
    > dict_entry = { 'key1':0, 'key2':0 }
    >
    > __init__(self):
    > for x in range(10):
    > tmp = copy.deepcopy(self.dict_entry)
    > tmp['key1'] = x
    > self.my_dict.append(tmp)
    > }
    >
    > in a driver, I have
    >
    > inst0, inst1 = A.A(), A.A()
    > inst0.my_dict[1]['key2'] = "ted"
    > inst1.my_dict[5]['key2'] = "steve"
    > inst0.display()
    > inst1.display()
    >
    > printing them out shows that both objects have ted and steve in their
    > dictionaries. I have done this very thing outside of a class, and it works
    > wonderfully. All other attributes in the class are able to remain unique.
    > ie, inst0.data = 5 and inst1.data = 8 works fine.
    > I believe I could set the dictionaries up beforehand, but they will be
    > substatially different sizes, and it certainly wouldn't be proper.
    >
    > Any ideas? Thanks very much for any insight.


    Kerry,

    two observations:

    1) That's not proper Python! ;-) and 2) I think the behaviours you want are
    only available in 'new style' classes, i.e. classes based upon 'object'. Did
    you mean:

    ---8<---
    class A(object):
    my_dict = []
    dict_entry = { 'key1':0, 'key2':0 }

    def __init__(self):
    for x in range(10):
    tmp = copy.deepcopy(self.dict_entry)
    tmp['key1'] = x
    self.my_dict.append(tmp)
    ---8<---

    A.my_dict is a /class variable/, i.e. it is tied to the class, not the
    instance - there's only one of them, viz:

    ---8<---
    >>> inst0,inst1=A(),A()
    >>> id(inst0),id(inst1) # get the id of the instances:

    (1083256236, 1083254732)
    >>> id(inst0.my_dict),id(inst1.my_dict) # and the id of their my_dict lists:

    (1083253228, 1083253228)
    >>> inst0.my_dict is inst1.my_dict

    True
    >>>

    ---8<---

    If you move the initial assignment of my_dict into the __init__() procedure,
    and call it self.my_dict() instead, then you'll get a new instance of a list
    each time - maybe this is what you want. BTW, is there any particular reason
    for choosing the word 'my_dict' to describe a list?

    hope this helps you arrive at a solution ;-)

    -andyj
    Andy Jewell, Nov 9, 2003
    #3
  4. > class A { ... } is *not* Python. Are you converting from Java :) ?
    > From what follows, I conclude that you want the dicitonary to be unique

    for
    > each *instance* (if you want it unique for the class, you are already
    > there). Unlike Java, you must put it in the constructor then (untested):
    >
    > class A:
    > def __init__(self):
    > self.my_dict = {"key1": 0, "key2": 0}
    >
    > Dictionaries are the {} thingies, [] indicate list literals. Deepcopy

    should
    > be buried very deep in the documentation, where no newbie will ever find
    > it...
    > Seriously, take the time to read the excellent tutorial that comes with
    > Python, and only then try to clean up this mess.



    hmm ... I didnt' word this quite right. I want a list [] of dictionaries
    {}. I mean for it to be similar to an array of structs in C, only using a
    dictionary instead of a struct and a list instead of an array. Seem
    reasonable?

    I have read the entire tutorial as well as "Learning Python" in its utter
    entirety. This is precisely how I know of deepcopy. I noticed that when I
    appended dictionaries to my list, modifying one modifed them all. I want
    each entry to be unique. I don't see how to do this without deepcopy. What
    really confuses me is that this same scenario works when not embedded in a
    class.

    As for your suggestion, putting the dictionary initialization and the list
    in the contructor did the trick. I had this:

    class a:
    x = 0
    y = 0
    :

    I suppose it's a c++ leftover thing, thinking i had to declare before I
    init. I don't yet understand why it's different for a simple data type than
    it is for the list of dictionaries. But by taking the dict_entry{} and
    my_dict[] out of the top of the class, it works without deepcopy. Thanks
    for the help.
    Kerry Neilson, Nov 9, 2003
    #4
  5. Kerry,

    two observations:

    1) That's not proper Python! ;-) and 2) I think the behaviours you want are
    only available in 'new style' classes, i.e. classes based upon 'object'. Did
    you mean:

    ---8<---
    class A(object):
    my_dict = []
    dict_entry = { 'key1':0, 'key2':0 }

    def __init__(self):
    for x in range(10):
    tmp = copy.deepcopy(self.dict_entry)
    tmp['key1'] = x
    self.my_dict.append(tmp)
    ---8<---

    A.my_dict is a /class variable/, i.e. it is tied to the class, not the
    instance - there's only one of them, viz:

    ---8<---
    >>> inst0,inst1=A(),A()
    >>> id(inst0),id(inst1) # get the id of the instances:

    (1083256236, 1083254732)
    >>> id(inst0.my_dict),id(inst1.my_dict) # and the id of their my_dict lists:

    (1083253228, 1083253228)
    >>> inst0.my_dict is inst1.my_dict

    True
    >>>

    ---8<---

    If you move the initial assignment of my_dict into the __init__() procedure,
    and call it self.my_dict() instead, then you'll get a new instance of a list
    each time - maybe this is what you want. BTW, is there any particular
    reason
    for choosing the word 'my_dict' to describe a list?

    hope this helps you arrive at a solution ;-)



    Thanks for the reply, Andy.
    Your observation was correct, as I also learned from Peter, and moving the
    initial assignment into __init__ is whta I wanted. I guess I didn't realize
    that one could do that, being more familiar with c++. And no, calling a
    list my_dict was pure accident. Perhaps my explanation in response to Peter
    explains it a bit better.

    Thanks.
    Kerry Neilson, Nov 9, 2003
    #5
  6. "Kerry Neilson" <> wrote in message
    news:gmxrb.114011$275.322447@attbi_s53...
    > Hi,
    > Really hung up on this one. I'm trying to get all the fields of a
    > dictionary to be unique for each class:
    >
    > class A {
    > my_dict = []
    > dict_entry = { 'key1':0, 'key2':0 }
    >
    > __init__(self):
    > for x in range(10):
    > tmp = copy.deepcopy(self.dict_entry)
    > tmp['key1'] = x
    > self.my_dict.append(tmp)
    > }


    I don't really understand what you're trying to do here, and it doesn't help
    that the syntax is completely wrong. Try pasting directly from real code or
    an interactive session in the future, and not trying to ad-lib an email
    message.

    I assume you mean this (watch me break my own advice):

    import copy
    class A:
    dictlist = []
    dictlist_elem = { 'key1':0, 'key2':0 }

    def __init__(self):
    for x in range(10):
    tmp = copy.deepcopy(self.dictlist_elem)
    tmp['key1'] = x
    self.dictlist.append(tmp)


    Now, this code makes no sense to me without knowing what you want to do.
    Init will just keep adding similar dictionaries to dictlist, which are
    accessable from all instances.

    >>> a = A()
    >>> [ D['key1'] for D in a.dictlist]

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> b = A()
    >>> [ D['key1'] for D in a.dictlist]

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> [ D['key2'] for D in a.dictlist]

    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    Is this what you want?

    > in a driver, I have
    >
    > inst0, inst1 = A.A(), A.A()
    > inst0.my_dict[1]['key2'] = "ted"
    > inst1.my_dict[5]['key2'] = "steve"
    > inst0.display()
    > inst1.display()


    So, class A is now in a module named A? And what does A.A.display()
    display? (You didn't include code for that method).

    I'll assume that the module detail is insignificant, and that display()
    prints dictlist.

    > printing them out shows that both objects have ted and steve in their
    > dictionaries.


    Well, yes. You declared your dictionary and dictionary lists as class
    attributes. Each instance does a lookup in itself, and not finding
    'dictlist' it looks in the class, where it's found.

    Ok, this point can often trip people up, so I'll go into more detail. (The
    following is for "classic" classes, which are on their way out. New-style
    classes are a bit different wrt instantiation and type.)

    In Python, classes are objects. So when you define class C, what you're
    saying is "make a class object which has these attributes and these methods,
    and make the name 'C' point to that object."

    >>> class C:

    .... someint = 12
    .... mylist = [1,2,3]
    ....
    >>> C

    <class __main__.C at 0x00AC5600>
    >>> C.someint

    12
    >>> C.mylist

    [1, 2, 3]
    >>>


    See? It's a real object with a real location in memory, not just a
    "template" for "real" instances. Classes are just as real as instances.

    When you make an instance, Python makes a new object (an instance) and then
    calls C.__init__(newinstanceobject). Init is an instance
    configurator--anything done in init is only done in the instance, not in the
    class.

    Indeed, all instances use the class's methods. instanceobject.dosomething()
    is really C.dosomething(instanceobject) (where C is determined at runtime,
    as the derived class which provides the method in question, after
    inheritence is taken into consideration). The "self" you add to each method
    object is the stand-in argument which will be the instance itself.

    Now, if you don't keep the above in mind, behavior like this becomes
    shocking:
    >> myinst = C()
    >>> myinst2 = C()
    >>> myinst.someint

    12
    >>> myinst.someint = 0
    >>> myinst.someint

    0
    >>> myinst2.someint #Ints work...

    12
    >>> myinst2.mylist.append(5)
    >>> myinst2.mylist

    [1, 2, 3, 5]
    >>> myinst.mylist #What the?!

    [1, 2, 3, 5]
    >>> C.mylist #Huh?

    [1, 2, 3, 5]

    When you do assignment on an instance, you are adding an attribute *to the
    instance*, not looking up that attribute in the instance/class hiearchy.
    But when you *modify* an attribute, Python *does* look for that attribute,
    does find it in the class object, and does modify it in the class object,
    *not* in the instance object!

    Assignment is fundamentally different from modification. Assignment makes a
    name point to an object. mylist[2] = 'something' makes the third item in
    mylist (an un-named name, if you will) point to 'something'. But
    mylist.dosomething() is radically different! It changes the object *in
    place*. Just to make a point of how different it is, any in-place
    object-modifying method will return None.

    > I have done this very thing outside of a class, and it works
    > wonderfully.


    I don't really know what this would look like outside of a class; I'm not
    really sure what "outside of a class" means.

    > All other attributes in the class are able to remain unique.
    > ie, inst0.data = 5 and inst1.data = 8 works fine.


    Why this happens should make sense now.

    > I believe I could set the dictionaries up beforehand, but they will be
    > substatially different sizes, and it certainly wouldn't be proper.
    >
    > Any ideas? Thanks very much for any insight.


    If only the subject of this message were the question, I would answer: "Just
    declare the dictionary in the class object of the instances." However, I
    suspect that the question in your mind means a thing different from what it
    means to Python, and that you will need to reclarify your problem.

    --
    Francis Avila
    Francis Avila, Nov 9, 2003
    #6
  7. On Sun, 09 Nov 2003 20:22:36 GMT, "Kerry Neilson" <> wrote:

    >Hi,
    >Really hung up on this one. I'm trying to get all the fields of a
    >dictionary to be unique for each class:
    >

    Please post something that at least compiles

    >>> class A {

    File "<stdin>", line 1
    class A {
    ^
    SyntaxError: invalid syntax


    >class A {

    ^--this must be left over from some abortive experiment
    > my_dict = []
    > dict_entry = { 'key1':0, 'key2':0 }
    >
    > __init__(self):
    > for x in range(10):
    > tmp = copy.deepcopy(self.dict_entry)
    > tmp['key1'] = x
    > self.my_dict.append(tmp)
    >}
    >
    >in a driver, I have
    >
    >inst0, inst1 = A.A(), A.A()

    This looks like you are not showing an import A, assuming that class A is defined in A.py

    >inst0.my_dict[1]['key2'] = "ted"
    >inst1.my_dict[5]['key2'] = "steve"
    >inst0.display()
    >inst1.display()
    >
    >printing them out shows that both objects have ted and steve in their

    I suggest you copy and paste from the session where you did that. Otherwise
    you're wasting your time and everyone else's.

    >dictionaries. I have done this very thing outside of a class, and it works
    >wonderfully. All other attributes in the class are able to remain unique.

    The above doesn't work period, so there is no telling what you actually did,
    and I might be able to guess what you are trying to do, but I don't really want to, sorry.

    >ie, inst0.data = 5 and inst1.data = 8 works fine.
    >I believe I could set the dictionaries up beforehand, but they will be
    >substatially different sizes, and it certainly wouldn't be proper.
    >
    >Any ideas? Thanks very much for any insight.
    >

    See above, guess I need a snack ;-)

    Regards,
    Bengt Richter
    Bengt Richter, Nov 9, 2003
    #7
  8. Kerry Neilson

    Peter Otten Guest

    Kerry Neilson wrote:

    > class a:
    > x = 0
    > y = 0
    > :
    >
    > I suppose it's a c++ leftover thing, thinking i had to declare before I
    > init. I don't yet understand why it's different for a simple data type
    > than
    > it is for the list of dictionaries. But by taking the dict_entry{} and
    > my_dict[] out of the top of the class, it works without deepcopy.


    class A:
    x = 100
    y = {}

    Here both assignments aren't declarations, but statements that are executed
    when the module containing them is imported.

    >>> A.x, A.y

    (100, {})
    >>>


    So they are both stored in the class. Let's play a little:

    >>> a = A()
    >>> a.x

    100

    >>> a.x = 200
    >>> a.x, A.x

    (200, 100)
    >>>


    You might suppose that a.x was initialized to A.x, but that's not the case:

    >>> del a.x
    >>> a.x

    100
    >>>


    See? The old value seems to reappear. When you say a.x and there is no
    instance attribute, Python looks up the class attribute with the same name.
    So you can use the class attribute as a kind of default value for the
    instance attribute.
    But beware, this technique bites you as soon as you try it with mutables:

    >>> a.y["key"] = "value"


    This looks the same, whether y is an instance or class attribute, and you
    would not complicate client code with some detection scheme.

    >>> A.y

    {'key': 'value'}
    >>>


    Oops, there was no y attribute in the instance, so we inadvertently changed
    y of the underlying class.

    By the way, for the same reason you must not use mutable values as default
    function arguments:

    def addwords(words, wordlist=[]):
    "Bad, don't do it"
    wordlist.extend(words.split())
    return wordlist

    The default wordlist is growing every time you don't provide a second
    argument. The canonical Python solution is

    def addwords2(words, wordlist=None):
    if wordlist is None: wordlist = []
    wordlist.extend(words.split())
    return wordlist

    which creates a new wordlist when the second argument is omitted.

    Peter
    Peter Otten, Nov 10, 2003
    #8
  9. > See? The old value seems to reappear. When you say a.x and there is no
    > instance attribute, Python looks up the class attribute with the same

    name.
    > So you can use the class attribute as a kind of default value for the
    > instance attribute.
    > But beware, this technique bites you as soon as you try it with mutables:
    >
    > >>> a.y["key"] = "value"

    >
    > This looks the same, whether y is an instance or class attribute, and you
    > would not complicate client code with some detection scheme.
    >
    > >>> A.y

    > {'key': 'value'}
    > >>>

    >
    > Oops, there was no y attribute in the instance, so we inadvertently

    changed
    > y of the underlying class.


    Thank you. Your message really cleared a lot of things up for me.
    Kerry Neilson, Nov 12, 2003
    #9
    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. RC
    Replies:
    1
    Views:
    387
    mortb
    Nov 11, 2004
  2. Mark Edwards
    Replies:
    1
    Views:
    310
    Ken Cox [Microsoft MVP]
    Feb 21, 2005
  3. John Wohlbier
    Replies:
    2
    Views:
    357
    Josiah Carlson
    Feb 22, 2004
  4. David McNab
    Replies:
    0
    Views:
    483
    David McNab
    Apr 9, 2004
  5. RajaniKumarV
    Replies:
    0
    Views:
    450
    RajaniKumarV
    Feb 22, 2010
Loading...

Share This Page