
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1602 fd906abe-77d9-0310-91a1-e0d9ade77398
153 lines
3.7 KiB
Text
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.
|