[Zope3-checkins] SVN: Zope3/trunk/src/zope/cachedescriptors/ Converted the tests to a doctest. Added lazy properties.

Jim Fulton jim at zope.com
Thu Dec 16 16:48:54 EST 2004


Log message for revision 28640:
  Converted the tests to a doctest. Added lazy properties.
  

Changed:
  U   Zope3/trunk/src/zope/cachedescriptors/README.txt
  U   Zope3/trunk/src/zope/cachedescriptors/__init__.py
  U   Zope3/trunk/src/zope/cachedescriptors/property.py
  A   Zope3/trunk/src/zope/cachedescriptors/property.txt
  D   Zope3/trunk/src/zope/cachedescriptors/tests/
  A   Zope3/trunk/src/zope/cachedescriptors/tests.py

-=-
Modified: Zope3/trunk/src/zope/cachedescriptors/README.txt
===================================================================
--- Zope3/trunk/src/zope/cachedescriptors/README.txt	2004-12-16 21:48:49 UTC (rev 28639)
+++ Zope3/trunk/src/zope/cachedescriptors/README.txt	2004-12-16 21:48:54 UTC (rev 28640)
@@ -1 +1,24 @@
-See __init__.py.
+Cached descriptors
+
+Cached descriptors cache their output.  They take into account
+instance attributes that they depend on, so when the instance
+attributes change, the descriptors will change the values they
+return.
+
+Cached descriptors cache their data in _v_ attributes, so they are
+also useful for managing the computation of volatile attributes for
+persistent objects.
+
+Persistent descriptors:
+
+  property
+
+     A simple computed property. See property.txt.
+
+  method
+
+     Idempotent method.  The return values are cached based on method
+     arguments and on any instance attributes that the methods are
+     defined to depend on.
+
+     **Note**, methods haven't been implemented yet.

Modified: Zope3/trunk/src/zope/cachedescriptors/__init__.py
===================================================================
--- Zope3/trunk/src/zope/cachedescriptors/__init__.py	2004-12-16 21:48:49 UTC (rev 28639)
+++ Zope3/trunk/src/zope/cachedescriptors/__init__.py	2004-12-16 21:48:54 UTC (rev 28640)
@@ -1,40 +1 @@
-##############################################################################
 #
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Cached descriptors
-
-Cached descriptors cache their output.  They take into account
-instance attributes that they depend on, so when the instance
-attributes change, the descriptors will change the values they
-return.
-
-Cached descriptors cache their data in _v_ attributes, so they are
-also useful for managing the computation of volatile attributes for
-persistent objects.
-
-Persistent descriptors:
-
-  property
-
-     A simple computed property
-
-  method
-
-     Idempotent method.  The return values are cached based on method
-     arguments and on any instance attributes that the methods are
-     defined to depend on.
-
-     **Note**, methods haven't been implemented yet.
-
-$Id$
-"""

Modified: Zope3/trunk/src/zope/cachedescriptors/property.py
===================================================================
--- Zope3/trunk/src/zope/cachedescriptors/property.py	2004-12-16 21:48:49 UTC (rev 28639)
+++ Zope3/trunk/src/zope/cachedescriptors/property.py	2004-12-16 21:48:54 UTC (rev 28640)
@@ -19,35 +19,6 @@
 
 class CachedProperty(object):
     """Cached Properties
-
-    Cached properties are computed properties that cache their computed
-    values.  They take into account instance attributes that they depend
-    on, so when the instance attributes change, the properties will change
-    the values they return.
-    
-    Cached properties cache their data in _v_ attributes, so they are
-    also useful for managing the computation of volatile attributes for
-    persistent objects.
-
-    Example::
-
-      from persistent import Persistent 
-      from zope.cachedescriptors.property import CachedProperty
-
-      class FileManager(Persistent):
-
-         def __init__(self, filename):
-             self.filename = filename
-
-         def file(self):
-             return open(self.filename)
-
-         file = CachedProperty(file, 'filename')
-
-      file_manager = FileManager('data.txt')
-
-      x = file_manager.file.read(10)
-
     """
 
     def __init__(self, func, *names):
@@ -79,3 +50,23 @@
         setattr(inst, value_name, value)
         
         return value
+
+
+class Lazy(object):
+    """Lazy Attributes
+    """
+
+    def __init__(self, func, name=None):
+        if name is None:
+            name = func.__name__
+        self.data = (func, name)
+
+    def __get__(self, inst, class_):
+        if inst is None:
+            return self
+
+        func, name = self.data
+        value = func(inst)
+        inst.__dict__[name] = value
+        
+        return value

Copied: Zope3/trunk/src/zope/cachedescriptors/property.txt (from rev 28622, Zope3/trunk/src/zope/cachedescriptors/property.py)
===================================================================
--- Zope3/trunk/src/zope/cachedescriptors/property.py	2004-12-10 19:58:18 UTC (rev 28622)
+++ Zope3/trunk/src/zope/cachedescriptors/property.txt	2004-12-16 21:48:54 UTC (rev 28640)
@@ -0,0 +1,128 @@
+Cached Properties
+=================
+
+Cached properties are computed properties that cache their computed
+values.  They take into account instance attributes that they depend
+on, so when the instance attributes change, the properties will change
+the values they return.
+
+Cached properties cache their data in _v_ attributes, so they are
+also useful for managing the computation of volatile attributes for
+persistent objects. Let's look at an example:
+
+    >>> from zope.cachedescriptors import property
+    >>> import math
+
+    >>> class Point:
+    ... 
+    ...     def __init__(self, x, y):
+    ...         self.x, self.y = x, y
+    ...
+    ...     def radius(self):
+    ...         print 'computing radius'
+    ...         return math.sqrt(self.x**2 + self.y**2)
+    ...     radius = property.CachedProperty(radius, 'x', 'y')
+
+    >>> point = Point(1.0, 2.0)   
+
+If we ask for the radius the first time:
+
+    >>> '%.2f' % point.radius
+    computing radius
+    '2.24'
+
+We see that the radius function is called, but if we ask for it again:
+
+    >>> '%.2f' % point.radius
+    '2.24'
+
+The function isn't called.  If we change one of the attribute the
+radius depends on, it will be recomputed:
+
+    >>> point.x = 2.0
+    >>> '%.2f' % point.radius
+    computing radius
+    '2.83'
+
+But changing other attributes doesn't cause recomputation:
+
+    >>> point.q = 1
+    >>> '%.2f' % point.radius
+    '2.83'
+
+Note that we don't have any non-volitile attributes added:
+
+    >>> names = [name for name in point.__dict__ if not name.startswith('_v_')]
+    >>> names.sort()
+    >>> names
+    ['q', 'x', 'y']
+
+Lazy Computed Attributes
+------------------------
+
+The `property` module provides another descriptor that supports a
+slightly different caching model: lazy attributes.  Like cached
+proprties, they are computed the first time they are used. however,
+they aren't stored in volatile attributes and they aren't
+automatically updated when other attributes change.  Furthermore, the
+store their data using their attribute name, thus overriding
+themselves. This provides much faster attribute access after the
+attribute has been computed. Let's look at the previous example using
+lazy attributes:
+
+    >>> class Point:
+    ... 
+    ...     def __init__(self, x, y):
+    ...         self.x, self.y = x, y
+    ...
+    ...     def radius(self):
+    ...         print 'computing radius'
+    ...         return math.sqrt(self.x**2 + self.y**2)
+    ...     radius = property.Lazy(radius)
+
+    >>> point = Point(1.0, 2.0)   
+
+If we ask for the radius the first time:
+
+    >>> '%.2f' % point.radius
+    computing radius
+    '2.24'
+
+We see that the radius function is called, but if we ask for it again:
+
+    >>> '%.2f' % point.radius
+    '2.24'
+
+The function isn't called.  If we change one of the attribute the
+radius depends on, it still isn't called:
+    
+    >>> point.x = 2.0
+    >>> '%.2f' % point.radius
+    '2.24'
+
+If we want the radius to be recomputed, we have to manually delete it:
+
+    >>> del point.radius
+
+    >>> point.x = 2.0
+    >>> '%.2f' % point.radius
+    computing radius
+    '2.83'
+
+Note that the radius is stored in the instance dictionary:
+
+    >>> '%.2f' % point.__dict__['radius']
+    '2.83'
+
+The lazy attribute needs to know the attribute name.  It normally
+deduces the attribute name from the name of the function passed. If we
+want to use a different name, we need to pass it:
+
+    >>> def d(point):
+    ...     print 'computing diameter'
+    ...     return 2*point.radius
+
+    >>> Point.diameter = property.Lazy(d, 'diameter')
+    >>> '%.2f' % point.diameter
+    computing diameter
+    '5.66'

Added: Zope3/trunk/src/zope/cachedescriptors/tests.py
===================================================================
--- Zope3/trunk/src/zope/cachedescriptors/tests.py	2004-12-16 21:48:49 UTC (rev 28639)
+++ Zope3/trunk/src/zope/cachedescriptors/tests.py	2004-12-16 21:48:54 UTC (rev 28640)
@@ -0,0 +1,27 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test hookup
+
+$Id$
+"""
+import unittest
+
+def test_suite():
+    from zope.testing import doctest
+    return doctest.DocFileSuite('property.txt')
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+
+



More information about the Zope3-Checkins mailing list