cybertools/util/property.txt
helmutm 53ae9f332a included rwproperty stuff in util.property
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1602 fd906abe-77d9-0310-91a1-e0d9ade77398
2007-03-01 08:46:45 +00:00

153 lines
3.7 KiB
Text

================
Smart Properties
================
$Id$
lzprop
======
The ``@lzprop`` decorator allows the declaration of lazy properties - attributes
that are calculated only on first access, and then just once. This is
extremely useful when working with objects of a limited lifetime (e.g.
adapters) that provide results from expensive calculations.
We use a simple class with one lazy property that tracks the calculation
by printing an informative message:
>>> from cybertools.util.property import lzprop
>>> class Demo(object):
... base = 6
... @lzprop
... def value(self):
... print 'calculating'
... return self.base * 7
>>> demo = Demo()
When we first access the `value` attribute the corresponding method will be
called:
>>> demo.value
calculating
42
On subsequent accesses the previously calculated value will be returned:
>>> demo.value
42
>>> demo.base = 15
>>> demo.value
42
Let's make sure the value is really calculated upon first access (and not
already during compilation or object creation):
>>> demo2 = Demo()
>>> demo2.base = 15
>>> demo2.value
calculating
105
>>> demo2.value
105
rwproperty
==========
:Author: Philipp von Weitershausen
:Email: philikon@philikon.de
:License: Zope Public License, v2.1
Goal
----
There should be a way to declare a read & write property and still use
the compact and easy decorator spelling. The read & write properties
should be as easy to use as the read-only property. We explicitly
don't want that immediately called function that really just helps us
name the attribute and create a local scope for the getter and setter.
Read & write property
---------------------
Read & write properties work like regular properties. You simply
define a method and then apply a decorator, except that you now don't
use ``@property`` but ``@getproperty`` to mark the getter and
``@setproperty`` to mark the setter:
>>> from cybertools.util.property import getproperty, setproperty, delproperty
>>> class JamesBrown(object):
... @getproperty
... def feel(self):
... return self._feel
... @setproperty
... def feel(self, feel):
... self._feel = feel
>>> i = JamesBrown()
>>> i.feel
Traceback (most recent call last):
...
AttributeError: 'JamesBrown' object has no attribute '_feel'
>>> i.feel = "good"
>>> i.feel
'good'
The order in which getters and setters are declared doesn't matter:
>>> class JamesBrown(object):
... @setproperty
... def feel(self, feel):
... self._feel = feel
... @getproperty
... def feel(self):
... return self._feel
>>> i = JamesBrown()
>>> i.feel = "good"
>>> i.feel
'good'
Of course, deleters are also possible:
>>> class JamesBrown(object):
... @setproperty
... def feel(self, feel):
... self._feel = feel
... @getproperty
... def feel(self):
... return self._feel
... @delproperty
... def feel(self):
... del self._feel
>>> i = JamesBrown()
>>> i.feel = "good"
>>> del i.feel
>>> i.feel
Traceback (most recent call last):
...
AttributeError: 'JamesBrown' object has no attribute '_feel'
Edge cases
----------
There might be a case where you're using a flavour of read & write
properties and already have a non-property attribute of the same name
defined:
>>> class JamesBrown(object):
... feel = "good"
... @getproperty
... def feel(self):
... return "so good"
...
Traceback (most recent call last):
...
TypeError: read & write properties cannot be mixed with other attributes except regular property objects.