Need help with simple OOP Python question

  • Thread starter Kristofer Tengström
  • Start date
K

Kristofer Tengström

Hi, I'm having trouble creating objects that in turn can have custom
objects as variables. The code looks like this:

---------------------------------------------

class A:
sub = dict()
def sub_add(self, cls):
obj = cls()
self.sub[obj.id] = obj

class B(A):
id = 'inst'

base = A()
base.sub_add(B)
base.sub['inst'].sub_add(B)

print # prints a blank line
print base.sub['inst']
print base.sub['inst'].sub['inst']

----------------------------------------------

Now, what I get from this is the following:
<__main__.B instance at 0x01FC20A8>
<__main__.B instance at 0x01FC20A8>
Why is this? What I want is for them to be two separate objects, but
it seems like they are the same one. I've tried very hard to get this
to work, but as I've been unsuccessful I would really appreciate some
comments on this. I'm sure it's something really easy that I just
haven't thought of.

Python version is 2.6.5 (I'm using Panda3D to create a 2½D game).
 
S

Stephen Hansen

Hi, I'm having trouble creating objects that in turn can have custom
objects as variables. The code looks like this:

You are sharing this single "sub" dictionary with all instances of your
A class.

If you want to define instance-specific attributes, define them in the
__init__ method, like so:

class A:
def __init__(self):
self.sub = dict()

def sub_add(self, cls):
obj = cls()
self.sub[obj.id] = obj

--

Stephen Hansen
... Also: Ixokai
... Mail: me+list/python (AT) ixokai (DOT) io
... Blog: http://meh.ixokai.io/


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.10 (Darwin)

iQEcBAEBAgAGBQJOZHVKAAoJEKcbwptVWx/l3+4H/3abOlp0rAR0HRYdxsDGw6HP
uZN9+H6mkw/NqgVsD5yxTkE+l1axvq6w0GmFsHBOm9Xx9dbnOpzAMuAENEPgxg5o
LXTwWVQePHnk2mr9xdwSkL5L/8cRqegzPaLVs085iMlnQUHkgO+l13grQD4Wnh6d
rXnV/Ah1y2I2iGO1bM382imEmGDkIUpM8tPdpnUyCVz1rI7LjsmvLoBspKKbT1Ip
qr8+1wvH9OINUff4YHBtIvST/wRhxQNYYOb7q6JcR88bGPhUXDNI73/eyVgIcGYf
Buft1Ieyl+RsweoEuGsXgGh+GuJN+ISBtlrrEFglSDPfsvHyP8jfeLisxjPX+IQ=
=tVRh
-----END PGP SIGNATURE-----
 
P

Peter Otten

Kristofer said:
Hi, I'm having trouble creating objects that in turn can have custom
objects as variables. The code looks like this:

Putting it into the class like this means sub is shared by all instances.
def sub_add(self, cls):
obj = cls()
self.sub[obj.id] = obj

class B(A):
id = 'inst'

base = A()
base.sub_add(B)
base.sub['inst'].sub_add(B)

print # prints a blank line
print base.sub['inst']
print base.sub['inst'].sub['inst']

----------------------------------------------

Now, what I get from this is the following:
<__main__.B instance at 0x01FC20A8>
<__main__.B instance at 0x01FC20A8>
Why is this? What I want is for them to be two separate objects, but
it seems like they are the same one. I've tried very hard to get this
to work, but as I've been unsuccessful I would really appreciate some
comments on this. I'm sure it's something really easy that I just
haven't thought of.

Your class A needs an initialiser:

class A:
def __init__(self):
self.sub = {} # one dict per instance
# ...
 
K

Kristofer Tengström

Thanks everyone, moving the declaration to the class's __init__ method
did the trick. Now there's just one little problem left. I'm trying to
create a list that holds the parents for each instance in the
hierarchy. This is what my code looks like now:

-----------------------------------------

class A:
def __init__(self, parents=None):
self.sub = dict()
if parents:
self.parents = parents
else:
self.parents = []
def sub_add(self, cls):
hierarchy = self.parents
hierarchy.append(self)
obj = cls(hierarchy)
self.sub[obj.id] = obj

class B(A):
id = 'inst'

base = A()
base.sub_add(B)
base.sub['inst'].sub_add(B)

print
print vars(base)
print
print vars(base.sub['inst'])
print
print vars(base.sub['inst'].sub['inst'])

---------------------------------------------

The output from this program is the following:

{'parents': [<__main__.A instance at 0x02179468>, <__main__.B instance
at 0x021794B8>], 'sub': {'inst': <__main__.B instance at 0x021794B8>}}

{'parents': [<__main__.A instance at 0x02179468>, <__main__.B instance
at 0x021794B8>], 'sub': {'inst': <__main__.B instance at 0x021794E0>}}

{'parents': [<__main__.A instance at 0x02179468>, <__main__.B instance
at 0x021794B8>], 'sub': {}}

As you can see, the problem looks similar to the one before: All the
instances have an identical parent list. However, I don't understand
why as self.parents is declared in the __init__ method. Any ideas?
What I want is for the first instance to have an empty list, the
second to have one element in the list and the third to have two
parent elements.
 
P

Peter Otten

Kristofer said:
Thanks everyone, moving the declaration to the class's __init__ method
did the trick. Now there's just one little problem left. I'm trying to
create a list that holds the parents for each instance in the
hierarchy. This is what my code looks like now:

-----------------------------------------

class A:
def __init__(self, parents=None):
self.sub = dict()
if parents:

You should explicitly test for None here; otherwise in a call like

ancestors = []
a = A(anchestors)

the list passed as an argument will not be used, which makes fore confusing
behaviour.
self.parents = parents
else:
self.parents = []
def sub_add(self, cls):
hierarchy = self.parents
hierarchy.append(self)

Here you are adding self to the parents (that should be called ancestors)
and pass it on to cls(...). Then -- because it's non-empty -- it will be
used by the child, too, and you end up with a single parents list.
obj = cls(hierarchy)
self.sub[obj.id] = obj

While the minimal fix is to pass a copy

def sub_add(self, cls):
obj = cls(self.parents + [self])
self.sub[obj.id] = obj

I suggest that you modify your node class to keep track only of the direct
parent instead of all ancestors. That makes the implementation more robust
when you move a node to another parent.
 
J

Jon Clements

Kristofer said:
Thanks everyone, moving the declaration to the class's __init__ method
did the trick. Now there's just one little problem left. I'm trying to
create a list that holds the parents for each instance in the
hierarchy. This is what my code looks like now:

class A:
    def __init__(self, parents=None):
        self.sub = dict()
        if parents:

You should explicitly test for None here; otherwise in a call like

ancestors = []
a = A(anchestors)

the list passed as an argument will not be used, which makes fore confusing
behaviour.
            self.parents = parents
        else:
            self.parents = []
    def sub_add(self, cls):
        hierarchy = self.parents
        hierarchy.append(self)

Here you are adding self to the parents (that should be called ancestors)
and pass it on to cls(...). Then -- because it's non-empty -- it will be
used by the child, too, and you end up with a single parents list.
        obj = cls(hierarchy)
        self.sub[obj.id] = obj

While the minimal fix is to pass a copy

def sub_add(self, cls):
    obj = cls(self.parents + [self])
    self.sub[obj.id] = obj

I suggest that you modify your node class to keep track only of the direct
parent instead of all ancestors. That makes the implementation more robust
when you move a node to another parent.

I may not be understanding the OP correctly, but going by what you've
put here, I might be tempted to take this kind of stuff out of the
class's and using a graph library (such as networkx) - that way if
traversal is necessary, it might be a lot easier. But once again, I
must say I'm not 100% sure what the OP wants to achieve...

Jon.
 
T

Terry Reedy

Thanks everyone, moving the declaration to the class's __init__ method
did the trick. Now there's just one little problem left. I'm trying to
create a list that holds the parents for each instance in the
hierarchy. This is what my code looks like now:

-----------------------------------------

class A:
def __init__(self, parents=None):
self.sub = dict()
if parents:
self.parents = parents
else:
self.parents = []
def sub_add(self, cls):
hierarchy = self.parents
hierarchy.append(self)
obj = cls(hierarchy)
self.sub[obj.id] = obj

Indexing objects by their internal id is usually useless. Considier
whether you should be using sets rather than dicts.
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top