sharing dictionaries amongst class instances

K

Kerry Neilson

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.
 
P

Peter Otten

Kerry said:
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
 
A

Andy Jewell

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<------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
 
K

Kerry Neilson

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.
 
K

Kerry Neilson

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<------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.
 
F

Francis Avila

Kerry Neilson said:
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."
.... someint = 12
.... 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.
 
B

Bengt Richter

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
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
 
P

Peter Otten

Kerry said:
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.

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

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

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:

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

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
 
K

Kerry Neilson

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.

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.
 

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,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top