list property fires get on append

I

ian

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
 
F

Fredrik Lundh

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

Soviut

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

Diez B. Roggisch

Soviut said:
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
 
C

Carl Banks

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
 
S

Soviut

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

Soviut

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.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,232
Latest member
DorotheaDo

Latest Threads

Top