[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

list property fires get on append

ian

1/6/2008 7:22:00 AM

I've created a class that has a property which points at a private
list. When I try to use the append() function on this list property,
the fget method is fired rather than the fset method. If I directly
set my property to a literal list, the set method fires.

Here's a stripped down version of my code:

class Hierarchy(object):
_children = []

def __init__(self):
return

def get_children(self):
print("GETTING")
return self._children

def set_children(self, value):
print("SETTING")
self._children = value

children = property(get_children, set_children)

-----USAGE------

import Hierarchy
hierarchy = Hierarchy.Hierarchy()
# this fires a get for some reason
hierarchy.children.append( Hierarchy.Hierarchy())
# this fires a set as expected
hierarchy.children = [Hierarchy.Hierarchy()]

------RESULT------

it prints:

GETTING
SETTING
6 Answers

Fredrik Lundh

1/6/2008 8:04:00 AM

0

ian@neustyle.com wrote:

> I've created a class that has a property which points at a private
> list. When I try to use the append() function on this list property,
> the fget method is fired rather than the fset method. If I directly
> set my property to a literal list, the set method fires.

> # this fires a get for some reason
> hierarchy.children.append( Hierarchy.Hierarchy())

that's the expected behaviour: you're *fetching* the "children"
attribute in order to modify it, you're not replacing it.

reading up on Python's object model might be helpful.

</F>

ian

1/6/2008 8:31:00 AM

0

On Jan 6, 3:03 am, Fredrik Lundh <fred...@pythonware.com> wrote:
> i...@neustyle.com wrote:
> > I've created a class that has a property which points at a private
> > list. When I try to use the append() function on this list property,
> > the fget method is fired rather than the fset method. If I directly
> > set my property to a literal list, the set method fires.
> > # this fires a get for some reason
> > hierarchy.children.append( Hierarchy.Hierarchy())
>
> that's the expected behaviour: you're *fetching* the "children"
> attribute in order to modify it, you're not replacing it.
>
> reading up on Python's object model might be helpful.
>
> </F>

I figured that an append would be treated as a set since I'm adding to
the list. But what you say makes sense, although I can't say I'm
happy with the behaviour. Is there any way I can get the append to
fire a set? I'm thinking of properties from my C# background where i
believe that manipulation such this would be considered a set.

Diez B. Roggisch

1/6/2008 11:52:00 AM

0

Soviut schrieb:
> On Jan 6, 3:03 am, Fredrik Lundh <fred...@pythonware.com> wrote:
>> i...@neustyle.com wrote:
>>> I've created a class that has a property which points at a private
>>> list. When I try to use the append() function on this list property,
>>> the fget method is fired rather than the fset method. If I directly
>>> set my property to a literal list, the set method fires.
>>> # this fires a get for some reason
>>> hierarchy.children.append( Hierarchy.Hierarchy())
>> that's the expected behaviour: you're *fetching* the "children"
>> attribute in order to modify it, you're not replacing it.
>>
>> reading up on Python's object model might be helpful.
>>
>> </F>
>
> I figured that an append would be treated as a set since I'm adding to
> the list. But what you say makes sense, although I can't say I'm
> happy with the behaviour. Is there any way I can get the append to
> fire a set? I'm thinking of properties from my C# background where i
> believe that manipulation such this would be considered a set.

No, it wouldn't. It's simply this:

c = a.b

Now what does that trigger? Obviously a get on a for the attribute b.
But what you do with c afterwards is totally up to you. You can alter
it, or you can leave it. Think of this example:

c = a.b
if random() > .5:
c.append(1)
else:
print c[0]

How should the interpreter or C# runtime know when a.b is executed how c
will be treated?

What you _can_ do is to not return the actual list-object in get, but
instead a proxy that will notify the owner of the list of all methods
called.


Diez

Carl Banks

1/6/2008 4:37:00 PM

0

On Sun, 06 Jan 2008 00:31:13 -0800, Soviut wrote:
> I figured that an append would be treated as a set since I'm adding to
> the list. But what you say makes sense, although I can't say I'm happy
> with the behaviour. Is there any way I can get the append to fire a
> set? I'm thinking of properties from my C# background where i believe
> that manipulation such this would be considered a set.

You'd have to have to hook into the container object itself to detect the
modification. This might be pretty workable for you since it's an
internal object. Basically, instead of using a list, use a special list-
like object that notifies it's owner when it changes. Here's a simple
example to point you in the right direction:


class NotifierList(list):
def __init__(self,*args,**kwargs):
super(NotifierList,self).__init__(*args,**kwargs)
self.watchers = []
def add_watcher(self,watcher):
self.watchers.append(watcher)
def _notify_watchers(self):
for watcher in self.watchers:
watcher.object_changed(self)
def append(self,value):
super(NotifierList,self).append(value)
self._notify_watchers()
# override all other mutating calls, including __setitem__
# left as exercise


class Hierarchy(object):
def __init__(self):
self.children = NotifierList()
self.children.add_watcher(self)
def object_changed(self,obj):
print "self.children modified"
# no need to make children a property then
# unless you want to trap rebinding it to new object also


A couple other minor suggestions:

print is a statement, not a function. You should write

print "GETTING"

not

print("GETTING")

The latter works, but it will cease to work if you want to print more
than one thing. Note that print is scheduled to become a function in
Python 3.0, but for now it's a statement.

Based on the name of your class and typical usage, I'm guessing that you
probably want _children to be an instance attribute rather than a class
attribute, so I redid it that way, but .)


P.S. Is calling a method called "firing" in C#?


Carl Banks

ian

1/7/2008 4:13:00 PM

0

On Jan 6, 11:36 am, Carl Banks <pavlovevide...@gmail.com> wrote:
> On Sun, 06 Jan 2008 00:31:13 -0800, Soviut wrote:
> > I figured that an append would be treated as a set since I'm adding to
> > the list. But what you say makes sense, although I can't say I'm happy
> > with the behaviour. Is there any way I can get the append to fire a
> > set? I'm thinking of properties from my C# background where i believe
> > that manipulation such this would be considered a set.
>
> You'd have to have to hook into the container object itself to detect the
> modification. This might be pretty workable for you since it's an
> internal object. Basically, instead of using a list, use a special list-
> like object that notifies it's owner when it changes. Here's a simple
> example to point you in the right direction:
>
> class NotifierList(list):
> def __init__(self,*args,**kwargs):
> super(NotifierList,self).__init__(*args,**kwargs)
> self.watchers = []
> def add_watcher(self,watcher):
> self.watchers.append(watcher)
> def _notify_watchers(self):
> for watcher in self.watchers:
> watcher.object_changed(self)
> def append(self,value):
> super(NotifierList,self).append(value)
> self._notify_watchers()
> # override all other mutating calls, including __setitem__
> # left as exercise
>
> class Hierarchy(object):
> def __init__(self):
> self.children = NotifierList()
> self.children.add_watcher(self)
> def object_changed(self,obj):
> print "self.children modified"
> # no need to make children a property then
> # unless you want to trap rebinding it to new object also
>
> A couple other minor suggestions:
>
> print is a statement, not a function. You should write
>
> print "GETTING"
>
> not
>
> print("GETTING")
>
> The latter works, but it will cease to work if you want to print more
> than one thing. Note that print is scheduled to become a function in
> Python 3.0, but for now it's a statement.
>
> Based on the name of your class and typical usage, I'm guessing that you
> probably want _children to be an instance attribute rather than a class
> attribute, so I redid it that way, but .)
>
> P.S. Is calling a method called "firing" in C#?
>
> Carl Banks

Thanks for the help, there's a lot to digest there but I realized that
I was having issues with _children being a class attribute when I
noticed every single instance had the same _children list. I've since
moved things into my __init__ method.

Basically the reason I needed to use a property was to run a private
helper method that sets references to the parent and root nodes of my
hierarchy. Since I was simply appending to my children list, there
was no callback to tell the container to process the children.

And no, calling a method is not necessarily called "firing", but I've
been using the term a lot recently when dealing with events so it
seemed appropriate.

ian

1/8/2008 3:49:00 PM

0

On Jan 6, 11:36 am, Carl Banks <pavlovevide...@gmail.com> wrote:
> On Sun, 06 Jan 2008 00:31:13 -0800, Soviut wrote:
> > I figured that an append would be treated as a set since I'm adding to
> > the list. But what you say makes sense, although I can't say I'm happy
> > with the behaviour. Is there any way I can get the append to fire a
> > set? I'm thinking of properties from my C# background where i believe
> > that manipulation such this would be considered a set.
>
> You'd have to have to hook into the container object itself to detect the
> modification. This might be pretty workable for you since it's an
> internal object. Basically, instead of using a list, use a special list-
> like object that notifies it's owner when it changes. Here's a simple
> example to point you in the right direction:
>
> class NotifierList(list):
> def __init__(self,*args,**kwargs):
> super(NotifierList,self).__init__(*args,**kwargs)
> self.watchers = []
> def add_watcher(self,watcher):
> self.watchers.append(watcher)
> def _notify_watchers(self):
> for watcher in self.watchers:
> watcher.object_changed(self)
> def append(self,value):
> super(NotifierList,self).append(value)
> self._notify_watchers()
> # override all other mutating calls, including __setitem__
> # left as exercise
>
> class Hierarchy(object):
> def __init__(self):
> self.children = NotifierList()
> self.children.add_watcher(self)
> def object_changed(self,obj):
> print "self.children modified"
> # no need to make children a property then
> # unless you want to trap rebinding it to new object also
>
> A couple other minor suggestions:
>
> print is a statement, not a function. You should write
>
> print "GETTING"
>
> not
>
> print("GETTING")
>
> The latter works, but it will cease to work if you want to print more
> than one thing. Note that print is scheduled to become a function in
> Python 3.0, but for now it's a statement.
>
> Based on the name of your class and typical usage, I'm guessing that you
> probably want _children to be an instance attribute rather than a class
> attribute, so I redid it that way, but .)
>
> P.S. Is calling a method called "firing" in C#?
>
> Carl Banks

I just want to thank you for the example. I implemented the
NotifierList class and modified it to suite my project. It works
great. Thanks again.