trap setting attribute when the attribute is dict

Discussion in 'Python' started by Guest, Sep 3, 2007.

  1. Guest

    Guest Guest

    Hello all,

    I have a question which might be simple or need some work around.

    I want to do something like this. My class/instance has a dict as a
    property. I want the instance to catch the change in the dict (change
    in some values, addition/deletion of key/value etc) to be recognized by
    the class instance.

    How can I do this? Any suggestions are very well appreciated.



    Here is an example of what I want my class to behave:

    class test(object):
    def __init__(self):
    self._d = {}
    self._changed = False
    def getd(self):
    print 'called getd'
    return self._d
    # dont know what to do next
    def setd(self,val):
    print 'called setd', key, val
    self._d[key] = val
    self._changed = True
    d = property(getd,setd,None,None)

    def getc(self):
    return self._changed
    changed = property(getc,None,None,None)

    if __name__ == '__main__':
    obj = test()
    print 'obj.changed = ', obj.changed
    print

    # I want obj to know that its propety d being changed here
    print "t.d['a'] = 1"
    obj.d['a'] = 1
    print

    # I want the "changed" property to be True
    print 'obj.changed = ', obj.changed









    --
    yosuke kimura
    Center for Energy and Environmental Resources
    The Univ. of Texas at Austin, USA
    Guest, Sep 3, 2007
    #1
    1. Advertising

  2. On Sep 3, 7:00 pm, <> wrote:
    > Hello all,
    >
    > I have a question which might be simple or need some work around.
    >
    > I want to do something like this. My class/instance has a dict as a
    > property. I want the instance to catch the change in the dict (change
    > in some values, addition/deletion of key/value etc) to be recognized by
    > the class instance.
    >
    > How can I do this? Any suggestions are very well appreciated.
    >
    > Here is an example of what I want my class to behave:
    >
    > class test(object):
    > def __init__(self):
    > self._d = {}
    > self._changed = False
    > def getd(self):
    > print 'called getd'
    > return self._d
    > # dont know what to do next
    > def setd(self,val):
    > print 'called setd', key, val
    > self._d[key] = val


    Where does the 'key' come from? Perhaps you need to look further into
    how properties work.

    > self._changed = True
    > d = property(getd,setd,None,None)
    >
    > def getc(self):
    > return self._changed
    > changed = property(getc,None,None,None)
    >
    > if __name__ == '__main__':
    > obj = test()
    > print 'obj.changed = ', obj.changed
    > print
    >
    > # I want obj to know that its propety d being changed here
    > print "t.d['a'] = 1"
    > obj.d['a'] = 1
    > print
    >
    > # I want the "changed" property to be True
    > print 'obj.changed = ', obj.changed
    >


    You can't do that with plain dicts, and I'm not sure it is often a
    good idea. However you could create a class that behaves like a dict
    but calls a function each time an item is set, e.g. (made up
    terminology):

    >>> class TriggerDict(object):

    .... def __init__(self, trigger, val=None):
    .... self.trigger = trigger
    .... self.dict_ = val or {}
    .... def __setitem__(self, key, val):
    .... self.trigger(self, key, val)
    .... self.dict_[key] = val
    .... def __getitem__(self, key):
    .... return self.dict_[key]
    ....
    >>> def trigger(d, k, v):

    .... print '%s => %s' % (k, v)
    ....
    >>> td = TriggerDict(trigger)
    >>> td['spanish'] = 'inquisition' # see side effect below.

    spanish => inquisition
    >>> td['spanish']

    'inquisition'
    >>>


    Obviously your trigger function would set the _changed attribute of
    the test object instead. And again it is probably not a good idea
    unless you know exactly what you are doing. If it was me, I'd try to
    rethink my design instead.

    HTH

    --
    Arnaud
    Arnaud Delobelle, Sep 3, 2007
    #2
    1. Advertising

  3. Guest

    Guest Guest

    Arnaud Delobelle <> wrote:
    > On Sep 3, 7:00 pm, <> wrote:
    >>
    >> I want to do something like this. My class/instance has a dict as a
    >> property. I want the instance to catch the change in the dict (change
    >> in some values, addition/deletion of key/value etc) to be recognized by
    >> the class instance.
    >>
    >> How can I do this? Any suggestions are very well appreciated.
    >>
    >> Here is an example of what I want my class to behave:
    >>
    >> class test(object):
    >> def __init__(self):
    >> self._d = {}
    >> self._changed = False
    >> def getd(self):
    >> print 'called getd'
    >> return self._d
    >> # dont know what to do next
    >> def setd(self,val):
    >> print 'called setd', key, val
    >> self._d[key] = val


    > Where does the 'key' come from? Perhaps you need to look further into
    > how properties work.


    This obviously doesn't work...

    >> self._changed = True
    >> d = property(getd,setd,None,None)
    >>
    >> def getc(self):
    >> return self._changed
    >> changed = property(getc,None,None,None)
    >>
    >> if __name__ == '__main__':
    >> obj = test()
    >> print 'obj.changed = ', obj.changed
    >> print
    >>
    >> # I want obj to know that its propety d being changed here
    >> print "t.d['a'] = 1"
    >> obj.d['a'] = 1
    >> print
    >>
    >> # I want the "changed" property to be True
    >> print 'obj.changed = ', obj.changed
    >>


    > You can't do that with plain dicts, and I'm not sure it is often a
    > good idea. However you could create a class that behaves like a dict
    > but calls a function each time an item is set, e.g. (made up
    > terminology):


    >>>> class TriggerDict(object):

    > ... def __init__(self, trigger, val=None):
    > ... self.trigger = trigger
    > ... self.dict_ = val or {}
    > ... def __setitem__(self, key, val):
    > ... self.trigger(self, key, val)
    > ... self.dict_[key] = val
    > ... def __getitem__(self, key):
    > ... return self.dict_[key]
    > ...
    >>>> def trigger(d, k, v):

    > ... print '%s => %s' % (k, v)
    > ...
    >>>> td = TriggerDict(trigger)
    >>>> td['spanish'] = 'inquisition' # see side effect below.

    > spanish => inquisition
    >>>> td['spanish']

    > 'inquisition'
    >>>>


    > Obviously your trigger function would set the _changed attribute of
    > the test object instead. And again it is probably not a good idea
    > unless you know exactly what you are doing. If it was me, I'd try to
    > rethink my design instead.


    Thank you for suggestion, Arnaud.

    Since you are discouraging this idea of trigger, may I ask an advice of
    if my intention was legitimate one or not?

    My intention was to have a propery 'sum' in my object, and which has sum
    of all the values() of the dict (i have code to make sure that the value
    of dict are all numeric). I could just the propery being calculated
    everytime the property got __getattr__. But I thought that it was
    inefficient to calculate the same number over and over when the value is
    already known. So I was thiking of calculating the number only when the
    dict got modified.

    I also experimented with the idea of subclassing dict itself to
    calculate the sum. Is this what would be better solution?

    Thank you again,



    > --
    > Arnaud




    --
    yosuke kimura
    Center for Energy and Environmental Resources
    The Univ. of Texas at Austin, USA
    Guest, Sep 3, 2007
    #3
  4. On Sep 3, 8:47 pm, <> wrote:

    [...]

    > My intention was to have a propery 'sum' in my object, and which has sum
    > of all the values() of the dict (i have code to make sure that the value
    > of dict are all numeric). I could just the propery being calculated
    > everytime the property got __getattr__. But I thought that it was
    > inefficient to calculate the same number over and over when the value is
    > already known. So I was thiking of calculating the number only when the
    > dict got modified.
    >
    > I also experimented with the idea of subclassing dict itself to
    > calculate the sum. Is this what would be better solution?


    Why not simply have a method to update the dictionary that also keeps
    the sum up to date? Something like that:

    >>> class MyObj(object):

    .... def __init__(self):
    .... self._d = {}
    .... self._sum = 0
    .... def set_key(self, key, val):
    .... self._sum += val - self._d.get(key, 0)
    .... self._d[key] = val
    .... def get_key(self, key):
    .... return self._d[key]
    .... def sum(self):
    .... return self._sum
    ....
    >>> a = MyObj()
    >>> a.set_key(1, 2)
    >>> a.sum()

    2
    >>> a.set_key('a', 10)
    >>> a.set_key(1, 5)
    >>> a.sum()

    15
    >>>


    Of course this is only worth it if you need to use the sum often
    enough.
    If you update the dictionary a lot but only need the sum from time to
    time, then it might not be worth it at all.

    Of course you could subclass dict:

    class MyDict(dict):
    def __init__(self, *args, **kwargs):
    self._sum = sum(self.itervalues())
    def __setitem__(self, key, val):
    self._sum += val - self.get(key, 0)
    dict.__setitem__(self, key, val)
    def sum(self):
    return self._sum
    # overload all other methods that mutate the dict
    # to keep _sum up to date

    >>> d = MyDict()
    >>> d.sum()

    0
    >>> d['a']=5
    >>> d.sum()

    5
    >>> d['b']=10
    >>> d['a']=8
    >>> d.sum()

    18
    >>>


    HTH

    --
    Arnaud
    Arnaud Delobelle, Sep 3, 2007
    #4
  5. Guest

    John Machin Guest

    On Sep 4, 5:47 am, <> wrote:
    > Arnaud Delobelle <> wrote:
    > > On Sep 3, 7:00 pm, <> wrote:

    >
    > >> I want to do something like this. My class/instance has a dict as a
    > >> property. I want the instance to catch the change in the dict (change
    > >> in some values, addition/deletion of key/value etc) to be recognized by
    > >> the class instance.

    >
    > >> How can I do this? Any suggestions are very well appreciated.

    >
    > >> Here is an example of what I want my class to behave:

    >
    > >> class test(object):
    > >> def __init__(self):
    > >> self._d = {}
    > >> self._changed = False
    > >> def getd(self):
    > >> print 'called getd'
    > >> return self._d
    > >> # dont know what to do next
    > >> def setd(self,val):
    > >> print 'called setd', key, val
    > >> self._d[key] = val

    > > Where does the 'key' come from? Perhaps you need to look further into
    > > how properties work.

    >
    > This obviously doesn't work...
    >
    >
    >
    > >> self._changed = True
    > >> d = property(getd,setd,None,None)

    >
    > >> def getc(self):
    > >> return self._changed
    > >> changed = property(getc,None,None,None)

    >
    > >> if __name__ == '__main__':
    > >> obj = test()
    > >> print 'obj.changed = ', obj.changed
    > >> print

    >
    > >> # I want obj to know that its propety d being changed here
    > >> print "t.d['a'] = 1"
    > >> obj.d['a'] = 1
    > >> print

    >
    > >> # I want the "changed" property to be True
    > >> print 'obj.changed = ', obj.changed

    >
    > > You can't do that with plain dicts, and I'm not sure it is often a
    > > good idea. However you could create a class that behaves like a dict
    > > but calls a function each time an item is set, e.g. (made up
    > > terminology):
    > >>>> class TriggerDict(object):

    > > ... def __init__(self, trigger, val=None):
    > > ... self.trigger = trigger
    > > ... self.dict_ = val or {}
    > > ... def __setitem__(self, key, val):
    > > ... self.trigger(self, key, val)
    > > ... self.dict_[key] = val
    > > ... def __getitem__(self, key):
    > > ... return self.dict_[key]
    > > ...
    > >>>> def trigger(d, k, v):

    > > ... print '%s => %s' % (k, v)
    > > ...
    > >>>> td = TriggerDict(trigger)
    > >>>> td['spanish'] = 'inquisition' # see side effect below.

    > > spanish => inquisition
    > >>>> td['spanish']

    > > 'inquisition'

    >
    > > Obviously your trigger function would set the _changed attribute of
    > > the test object instead. And again it is probably not a good idea
    > > unless you know exactly what you are doing. If it was me, I'd try to
    > > rethink my design instead.

    >
    > Thank you for suggestion, Arnaud.
    >
    > Since you are discouraging this idea of trigger, may I ask an advice of
    > if my intention was legitimate one or not?
    >
    > My intention was to have a propery 'sum' in my object, and which has sum
    > of all the values() of the dict (i have code to make sure that the value
    > of dict are all numeric). I could just the propery being calculated
    > everytime the property got __getattr__. But I thought that it was
    > inefficient to calculate the same number over and over when the value is
    > already known. So I was thiking of calculating the number only when the
    > dict got modified.
    >
    > I also experimented with the idea of subclassing dict itself to
    > calculate the sum. Is this what would be better solution?
    >


    And what's wrong with calculating the value when you need it ...

    def thedictsum(self):
    return sum(self.thedict.itervalues())

    How big will the dict be, how often will it be updated, how often will
    the sum be required?

    Have you done timings of any of the options?

    You say you have code to check that the values are numeric; why not
    put this together with maintenance of the sum into a method which
    clients must use to update the dict?

    BTW, my motto:

    "__" == "Danger, Will Robinson!" == "Wrong way, go back" :)

    HTH,
    John
    John Machin, Sep 4, 2007
    #5
  6. Guest

    Guest Guest

    Arnaud Delobelle <> wrote:
    > On Sep 3, 8:47 pm, <> wrote:


    > [...]


    >> My intention was to have a propery 'sum' in my object, and which has sum
    >> of all the values() of the dict (i have code to make sure that the value
    >> of dict are all numeric). I could just the propery being calculated
    >> everytime the property got __getattr__. But I thought that it was
    >> inefficient to calculate the same number over and over when the value is
    >> already known. So I was thiking of calculating the number only when the
    >> dict got modified.
    >>
    >> I also experimented with the idea of subclassing dict itself to
    >> calculate the sum. Is this what would be better solution?


    > Why not simply have a method to update the dictionary that also keeps
    > the sum up to date? Something like that:


    >>>> class MyObj(object):

    > ... def __init__(self):
    > ... self._d = {}
    > ... self._sum = 0
    > ... def set_key(self, key, val):
    > ... self._sum += val - self._d.get(key, 0)
    > ... self._d[key] = val
    > ... def get_key(self, key):
    > ... return self._d[key]
    > ... def sum(self):
    > ... return self._sum
    > ...
    >>>> a = MyObj()
    >>>> a.set_key(1, 2)
    >>>> a.sum()

    > 2
    >>>> a.set_key('a', 10)
    >>>> a.set_key(1, 5)
    >>>> a.sum()

    > 15
    >>>>


    > Of course this is only worth it if you need to use the sum often
    > enough.
    > If you update the dictionary a lot but only need the sum from time to
    > time, then it might not be worth it at all.


    > Of course you could subclass dict:


    > class MyDict(dict):
    > def __init__(self, *args, **kwargs):
    > self._sum = sum(self.itervalues())
    > def __setitem__(self, key, val):
    > self._sum += val - self.get(key, 0)
    > dict.__setitem__(self, key, val)
    > def sum(self):
    > return self._sum
    > # overload all other methods that mutate the dict
    > # to keep _sum up to date


    >>>> d = MyDict()
    >>>> d.sum()

    > 0
    >>>> d['a']=5
    >>>> d.sum()

    > 5
    >>>> d['b']=10
    >>>> d['a']=8
    >>>> d.sum()

    > 18
    >>>>


    Thank you. I will go with subclassing method.

    --
    yosuke kimura
    Center for Energy and Environmental Resources
    The Univ. of Texas at Austin, USA
    Guest, Sep 4, 2007
    #6
  7. Guest

    Guest Guest

    John Machin <> wrote:
    > On Sep 4, 5:47 am, <> wrote:
    >> Arnaud Delobelle <> wrote:
    >> > On Sep 3, 7:00 pm, <> wrote:

    >>
    >> >> I want to do something like this. My class/instance has a dict as a
    >> >> property. I want the instance to catch the change in the dict (change
    >> >> in some values, addition/deletion of key/value etc) to be recognized by
    >> >> the class instance.

    >>
    >> >> How can I do this? Any suggestions are very well appreciated.

    >>


    [snip]

    >>
    >> Thank you for suggestion, Arnaud.
    >>
    >> Since you are discouraging this idea of trigger, may I ask an advice of
    >> if my intention was legitimate one or not?
    >>
    >> My intention was to have a propery 'sum' in my object, and which has sum
    >> of all the values() of the dict (i have code to make sure that the value
    >> of dict are all numeric). I could just the propery being calculated
    >> everytime the property got __getattr__. But I thought that it was
    >> inefficient to calculate the same number over and over when the value is
    >> already known. So I was thiking of calculating the number only when the
    >> dict got modified.
    >>
    >> I also experimented with the idea of subclassing dict itself to
    >> calculate the sum. Is this what would be better solution?
    >>


    > And what's wrong with calculating the value when you need it ...


    > def thedictsum(self):
    > return sum(self.thedict.itervalues())


    > How big will the dict be, how often will it be updated, how often will
    > the sum be required?


    > Have you done timings of any of the options?


    Thank you for the post.

    I want the code to have fewer lines of code. That was my biggest
    motivation for this. I have many of this dict with sum, each have <1000
    elements. all of them share the same keys. I do many
    addition/subtraction/divisions by each elements (yes, division makes the
    elements not being able to summed). so in my code i have repeated lines
    that works on each elements and then on the sum. i made a dict to take
    care of adding elements by elements. but i still had two lines of code,
    one for dict elements, and one for the sum.

    my code was originally in fortran and it has like thousands of lines,
    obscuring the mathematics that i want to do. i need to modify math
    often while i use the code (i dont know exactly what math i will need at
    this point), so i want to the code to be able to show the math
    clearly. ideally i want to this to be somthing like

    c = a + b
    d = 1 / c

    and each of a, b, c, d knows how to operates on each elements, and also
    keep track of the sum.

    I think i will settle for extending dict to have sum property and keep
    track of it .





    > You say you have code to check that the values are numeric; why not
    > put this together with maintenance of the sum into a method which
    > clients must use to update the dict?


    > BTW, my motto:


    > "__" == "Danger, Will Robinson!" == "Wrong way, go back" :)



    not quite getting this.... :(

    > HTH,
    > John



    --
    yosuke kimura
    Center for Energy and Environmental Resources
    The Univ. of Texas at Austin, USA
    Guest, Sep 4, 2007
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Skip Montanaro
    Replies:
    0
    Views:
    403
    Skip Montanaro
    Aug 15, 2003
  2. Alexander Kozlovsky

    dict!ident as equivalent of dict["ident"]

    Alexander Kozlovsky, May 21, 2006, in forum: Python
    Replies:
    5
    Views:
    345
    Alexander Kozlovsky
    May 22, 2006
  3. Paul Melis

    dict.has_key(x) versus 'x in dict'

    Paul Melis, Dec 6, 2006, in forum: Python
    Replies:
    48
    Views:
    1,297
    Kent Johnson
    Dec 15, 2006
  4. Almad
    Replies:
    8
    Views:
    386
    Terry Reedy
    Dec 14, 2006
  5. Drew
    Replies:
    19
    Views:
    1,328
    Duncan Booth
    Mar 15, 2007
Loading...

Share This Page