One-Shot Property?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Kevin Smith

    #1

    One-Shot Property?


    I have many cases in my code where I use a property for calculating a
    value on-demand. Quite a few of these only need to be called once.
    After that the value is always the same. In these properties, I set a
    variable in the instance as a cached value and return that value on
    subsequent calls. It would be nice if there was a descriptor that would
    do this automatically. Actually, what would be really nice is if I
    could replace the property altogether and put the calculated value in
    its place after the first call, but the property itself prevents me from
    doing that. Is this possible?

    --
    Kevin Smith
    Kevin.Smith@sas .com
  • Dave Benjamin

    #2
    Re: One-Shot Property?

    Kevin Smith wrote:[color=blue]
    > I have many cases in my code where I use a property for calculating a
    > value on-demand. Quite a few of these only need to be called once.
    > After that the value is always the same. In these properties, I set a
    > variable in the instance as a cached value and return that value on
    > subsequent calls. It would be nice if there was a descriptor that would
    > do this automatically. Actually, what would be really nice is if I
    > could replace the property altogether and put the calculated value in
    > its place after the first call, but the property itself prevents me from
    > doing that. Is this possible?[/color]

    I was going to recommend taking a look at the "memoize" example on the
    Python wiki, but it seems to be missing at the moment. In any case,
    here's the URL:

    The official home of the Python Programming Language


    There are also a few examples on the Cookbook, like this one:



    It shouldn't be too difficult to adapt this technique so that it can be
    used to create properties. I wouldn't bother replacing the property with
    an attribute unless you have a specific reason to do so (performance,
    perhaps).

    HTH,
    Dave

    Comment

    • Daniel Dittmar

      #3
      Re: One-Shot Property?

      Kevin Smith wrote:[color=blue]
      > I have many cases in my code where I use a property for calculating a
      > value on-demand. Quite a few of these only need to be called once.
      > After that the value is always the same. In these properties, I set a
      > variable in the instance as a cached value and return that value on
      > subsequent calls. It would be nice if there was a descriptor that would
      > do this automatically. Actually, what would be really nice is if I
      > could replace the property altogether and put the calculated value in
      > its place after the first call, but the property itself prevents me from
      > doing that. Is this possible?[/color]

      If you use the old-fashioned __getattr__ method instead of properties.
      __getattr__ gets called only if the value can't be found in the instance
      dictionary.

      def __getattr__ (self, attrname):
      try:
      method = getattr (self, 'calculate_' + attrname)
      except AttributeError:
      raise AttributeError, attrname
      value = method ()
      setattr (self, attrname, value)
      return value

      And probably also through metaclasses. And decorators.

      Daniel

      Comment

      • Leif K-Brooks

        #4
        Re: One-Shot Property?

        Kevin Smith wrote:[color=blue]
        > I have many cases in my code where I use a property for calculating a
        > value on-demand. Quite a few of these only need to be called once.
        > After that the value is always the same. In these properties, I set a
        > variable in the instance as a cached value and return that value on
        > subsequent calls. It would be nice if there was a descriptor that would
        > do this automatically. Actually, what would be really nice is if I
        > could replace the property altogether and put the calculated value in
        > its place after the first call, but the property itself prevents me from
        > doing that.[/color]

        This should do it:

        class CachingProperty (object):
        def __init__(self, attr_name, calculate_funct ion):
        self._name = attr_name
        self._calculate = calculate_funct ion

        def __get__(self, obj, type=None):
        if obj is None:
        return self
        else:
        value = self._calculate (obj)
        setattr(obj, self._name, value)
        return value

        And example code:
        [color=blue][color=green][color=darkred]
        >>> class Foo(object):[/color][/color][/color]
        .... def calculate_value (self):
        .... print 'Calculating... '
        .... return 42
        .... foo = CachingProperty ('foo', calculate_value )
        ....[color=blue][color=green][color=darkred]
        >>> bar = Foo()
        >>> bar.__dict__[/color][/color][/color]
        {}[color=blue][color=green][color=darkred]
        >>> bar.foo[/color][/color][/color]
        Calculating...
        42[color=blue][color=green][color=darkred]
        >>> bar.foo # Notice that the print statement doesn't run this time[/color][/color][/color]
        42[color=blue][color=green][color=darkred]
        >>> bar.__dict__[/color][/color][/color]
        {'foo': 42}

        Comment

        • John Lenton

          #5
          Re: One-Shot Property?

          On Tue, Jan 18, 2005 at 04:54:56PM +0000, Kevin Smith wrote:[color=blue]
          >
          > I have many cases in my code where I use a property for calculating a
          > value on-demand. Quite a few of these only need to be called once.
          > After that the value is always the same. In these properties, I set a
          > variable in the instance as a cached value and return that value on
          > subsequent calls. It would be nice if there was a descriptor that would
          > do this automatically. Actually, what would be really nice is if I
          > could replace the property altogether and put the calculated value in
          > its place after the first call, but the property itself prevents me from
          > doing that. Is this possible?[/color]

          consider this:

          1 >>> class C:
          2 ... x = property(str)
          3 ...
          4 >>> c = C()
          5 >>> c.x
          6 '<__main__.C instance at 0x4008d92c>'
          7 >>> setattr(c, 'x', c.x)
          8 >>> c.x
          9 '<__main__.C instance at 0x4008d92c>'
          10 >>> C.x
          11 <property object at 0x4009a7ac>
          12 >>> c.x = 2
          13 >>>

          in line 5 you see that the x property so defined works. In line 7 you
          remove it, replacing it with the computed value of the property. Line
          9 shows that it worked, line 11 shows that it didn't break the class,
          and line 13 (through the absence of an exception) shows that it no
          longer is 'special' (as it shouldn't be).

          --
          John Lenton (john@grulic.or g.ar) -- Random fortune:
          You can tune a piano, but you can't tuna fish.

          You can tune a filesystem, but you can't tuna fish.
          -- from the tunefs(8) man page

          -----BEGIN PGP SIGNATURE-----
          Version: GnuPG v1.2.5 (GNU/Linux)

          iD8DBQFB7VZmgPq u395ykGsRApG8AK CWsaaRwbKN8gWCQ w2E1gXryWCpZACg rbHz
          CjrrwkO6QkZ1ziM G6GKXQug=
          =+cST
          -----END PGP SIGNATURE-----

          Comment

          • Scott David Daniels

            #6
            Re: One-Shot Property?

            Leif K-Brooks wrote:[color=blue]
            >
            > class CachingProperty (object):
            > def __init__(self, attr_name, calculate_funct ion):
            > self._name = attr_name
            > self._calculate = calculate_funct ion
            > def __get__(self, obj, type=None):
            > if obj is None:
            > return self
            > else:
            > value = self._calculate (obj)
            > setattr(obj, self._name, value)
            > return value
            >
            > And example code:[color=green][color=darkred]
            > >>> class Foo(object):[/color][/color]
            > ... def calculate_value (self):
            > ... print 'Calculating... '
            > ... return 42
            > ... foo = CachingProperty ('foo', calculate_value )
            > ...[color=green][color=darkred]
            > >>> bar = Foo()
            > >>> bar.__dict__[/color][/color]
            > {}[color=green][color=darkred]
            > >>> bar.foo[/color][/color]
            > Calculating...
            > 42[color=green][color=darkred]
            > >>> bar.foo # Notice that the print statement doesn't run this time[/color][/color]
            > 42[color=green][color=darkred]
            > >>> bar.__dict__[/color][/color]
            > {'foo': 42}[/color]


            To build on this for Python 2.4:

            class Caches(object):
            def __init__(self, calculate_funct ion):
            self._calculate = calculate_funct ion

            def __get__(self, obj, _=None):
            if obj is None:
            return self
            value = self._calculate (obj)
            setattr(obj, self._calculate .func_name, value)
            return value


            class Foo(object):
            @Caches
            def foo(self):
            print 'Calculating... '
            return 42

            --Scott David Daniels
            Scott.Daniels@A cm.Org

            Comment

            • Terry Hancock

              #7
              Re: One-Shot Property?

              On Tuesday 18 January 2005 12:33 pm, John Lenton wrote:[color=blue]
              >
              > consider this:
              >
              > 1 >>> class C:
              > 2 ... x = property(str)
              > 3 ...
              > 4 >>> c = C()
              > 5 >>> c.x
              > 6 '<__main__.C instance at 0x4008d92c>'
              > 7 >>> setattr(c, 'x', c.x)
              > 8 >>> c.x
              > 9 '<__main__.C instance at 0x4008d92c>'
              > 10 >>> C.x
              > 11 <property object at 0x4009a7ac>
              > 12 >>> c.x = 2
              > 13 >>>
              >
              > in line 5 you see that the x property so defined works. In line 7 you
              > remove it, replacing it with the computed value of the property. Line
              > 9 shows that it worked, line 11 shows that it didn't break the class,
              > and line 13 (through the absence of an exception) shows that it no
              > longer is 'special' (as it shouldn't be).[/color]

              It wasn't "special" before, either -- I tried this myself, because I wasn't
              familiar with properties yet, and I was trying to figure out what you
              meant by that.

              The catch is that I think you meant to type:
              [color=blue]
              > 1 >>> class C(object):[/color]

              (i.e. so that C is a "new-style" class).

              Then we get "special" behavior (which answers my first question):
              [color=blue][color=green][color=darkred]
              >>> class C(object):[/color][/color][/color]
              .... x = property(str)
              ....[color=blue][color=green][color=darkred]
              >>> C.x[/color][/color][/color]
              <property object at 0x401e8cd4>[color=blue][color=green][color=darkred]
              >>> c = C()
              >>> c.x[/color][/color][/color]
              '<__main__.C object at 0x401e984c>'[color=blue][color=green][color=darkred]
              >>> c.x = 2[/color][/color][/color]
              Traceback (most recent call last):
              File "<stdin>", line 1, in ?
              AttributeError: can't set attribute[color=blue][color=green][color=darkred]
              >>>[/color][/color][/color]

              Unfortunately this seems to break your technique, too:
              [color=blue][color=green][color=darkred]
              >>> setattr(c, 'x', c.x)[/color][/color][/color]
              Traceback (most recent call last):
              File "<stdin>", line 1, in ?
              AttributeError: can't set attribute

              Too bad. I was kind of hoping it was just a typo. :-(

              Unless I'm missing something, anyway.

              Cheers,
              Terry

              --
              --
              Terry Hancock ( hancock at anansispacework s.com )
              Anansi Spaceworks http://www.anansispaceworks.com

              Comment

              Working...