Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #12758 > unrolled thread

Need help with simple OOP Python question

Started byKristofer Tengström <krille012@gmail.com>
First post2011-09-04 23:47 -0700
Last post2011-09-08 12:53 +0200
Articles 10 — 7 participants

Back to article view | Back to comp.lang.python


Contents

  Need help with simple OOP Python question Kristofer Tengström <krille012@gmail.com> - 2011-09-04 23:47 -0700
    Re: Need help with simple OOP Python question Stephen Hansen <me+list/python@ixokai.io> - 2011-09-05 00:07 -0700
    Re: Need help with simple OOP Python question Peter Otten <__peter__@web.de> - 2011-09-05 09:10 +0200
    Re: Need help with simple OOP Python question Ben Finney <ben+python@benfinney.id.au> - 2011-09-05 17:26 +1000
      Re: Need help with simple OOP Python question Kristofer Tengström <krille012@gmail.com> - 2011-09-05 06:15 -0700
        Re: Need help with simple OOP Python question Peter Otten <__peter__@web.de> - 2011-09-05 16:43 +0200
          Re: Need help with simple OOP Python question Jon Clements <joncle@googlemail.com> - 2011-09-05 07:59 -0700
            Re: Need help with simple OOP Python question Peter Otten <__peter__@web.de> - 2011-09-05 17:28 +0200
        Re: Need help with simple OOP Python question Terry Reedy <tjreedy@udel.edu> - 2011-09-05 13:38 -0400
          Re: Need help with simple OOP Python question Piet van Oostrum <piet@vanoostrum.org> - 2011-09-08 12:53 +0200

#12758 — Need help with simple OOP Python question

FromKristofer Tengström <krille012@gmail.com>
Date2011-09-04 23:47 -0700
SubjectNeed help with simple OOP Python question
Message-ID<dce02da0-c9c9-4331-aa2e-7d99f5e26cd1@g9g2000yqb.googlegroups.com>
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).

[toc] | [next] | [standalone]


#12759

FromStephen Hansen <me+list/python@ixokai.io>
Date2011-09-05 00:07 -0700
Message-ID<mailman.766.1315206481.27778.python-list@python.org>
In reply to#12758

[Multipart message — attachments visible in raw view] — view raw

On 9/4/11 11:47 PM, Kristofer Tengström wrote:
> 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()

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/

[toc] | [prev] | [next] | [standalone]


#12760

FromPeter Otten <__peter__@web.de>
Date2011-09-05 09:10 +0200
Message-ID<mailman.767.1315206626.27778.python-list@python.org>
In reply to#12758
Kristofer Tengström wrote:

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

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

[toc] | [prev] | [next] | [standalone]


#12762

FromBen Finney <ben+python@benfinney.id.au>
Date2011-09-05 17:26 +1000
Message-ID<87sjobo3es.fsf@benfinney.id.au>
In reply to#12758
Kristofer Tengström <krille012@gmail.com> writes:

> Hi, I'm having trouble creating objects that in turn can have custom
> objects as variables.

That terminology is rather confused.

I think what you want is to have instances with their own attributes.

> class A:
>     sub = dict()

This binds a single object (a new empty dict) to the class attribute
‘sub’. Every instance of class ‘A’ will share the same attribute, and
hence that same dict.

>     def sub_add(self, cls):

This defines a function which will be bound to the class attribute
‘sub_add’. It will, when later called as a method, receive the instance
as the first parameter, bound to the local name ‘self’.

>         obj = cls()
>         self.sub[obj.id] = obj

Here, ‘self’ will be an instance of the ‘A’ class. Each instance has no
‘sub’ attribute, so Python will find the class attribute ‘A.sub’, shared
by all ‘A’ instances. You're then modifying that class attribute ‘A.sub’.

[…]

> Now, what I get from this is the following:
> <__main__.B instance at 0x01FC20A8>
> <__main__.B instance at 0x01FC20A8>
> Why is this?

I hope the above explains it.

> What I want is for them to be two separate objects, but it seems like
> they are the same one.

Yes. Anything you talk about in the class definition scope cannot know
about any instance of that class, since the instances don't exist yet.

Instead, instance attributes need to be bound to a particular instance,
which means you need to have a reference to the instance; that's what
‘self’ is for. The class initialiser is a method named ‘__init__’, and
is called on each newly-created instance before that instance is
returned from the constructor.

I advise you to work through the Python tutorial, beginning to end,
which will give you a good grounding in these and other fundamental
Python topics <URL:http://docs.python.org/tutorial/>. Work through each
example, understand it by experimenting, and then proceed to the next,
until you've done the lot.

-- 
 \         “If history and science have taught us anything, it is that |
  `\     passion and desire are not the same as truth.” —E. O. Wilson, |
_o__)                                              _Consilience_, 1998 |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#12767

FromKristofer Tengström <krille012@gmail.com>
Date2011-09-05 06:15 -0700
Message-ID<93d65d9e-8a15-4423-94c0-3d385def24ed@et6g2000vbb.googlegroups.com>
In reply to#12762
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.

[toc] | [prev] | [next] | [standalone]


#12770

FromPeter Otten <__peter__@web.de>
Date2011-09-05 16:43 +0200
Message-ID<j42n5g$qm5$1@solani.org>
In reply to#12767
Kristofer Tengström wrote:

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

[toc] | [prev] | [next] | [standalone]


#12775

FromJon Clements <joncle@googlemail.com>
Date2011-09-05 07:59 -0700
Message-ID<9bda87ee-ac0a-4b8f-b9a9-aa0cefd1bd04@k9g2000vbd.googlegroups.com>
In reply to#12770
On Sep 5, 3:43 pm, Peter Otten <__pete...@web.de> wrote:
> Kristofer Tengström wrote:
> > 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.

[toc] | [prev] | [next] | [standalone]


#12779

FromPeter Otten <__peter__@web.de>
Date2011-09-05 17:28 +0200
Message-ID<mailman.776.1315236535.27778.python-list@python.org>
In reply to#12775
Jon Clements wrote:

> I
> must say I'm not 100% sure what the OP wants to achieve...

Learn Python? 

;)

[toc] | [prev] | [next] | [standalone]


#12785

FromTerry Reedy <tjreedy@udel.edu>
Date2011-09-05 13:38 -0400
Message-ID<mailman.779.1315244354.27778.python-list@python.org>
In reply to#12767
On 9/5/2011 9:15 AM, Kristofer Tengström wrote:
> 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.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#12955

FromPiet van Oostrum <piet@vanoostrum.org>
Date2011-09-08 12:53 +0200
Message-ID<m2r53rb8zq.fsf@cochabamba.vanoostrum.org>
In reply to#12785
Terry Reedy <tjreedy@udel.edu> writes:

> Indexing objects by their internal id is usually useless. 

obj.id is not the internal id. 
-- 
Piet van Oostrum <piet@vanoostrum.org>
WWW: http://pietvanoostrum.com/
PGP key: [8DAE142BE17999C4]

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web