included rwproperty stuff in util.property
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1602 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
b4dd7ccddd
commit
53ae9f332a
2 changed files with 177 additions and 3 deletions
|
@ -17,9 +17,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Advanced properties, esp the lazy one...
|
Advanced properties, i.e. the lazy one and Philipp von Weitershausen's
|
||||||
|
read & write properties.
|
||||||
|
|
||||||
Based on zope.cachedescriptors.property.Lazy
|
Based on zope.cachedescriptors.property.Lazy and including code from
|
||||||
|
http://cheeseshop.python.org/pypi/rwproperty.
|
||||||
|
|
||||||
$Id$
|
$Id$
|
||||||
"""
|
"""
|
||||||
|
@ -39,3 +41,76 @@ class lzprop(object):
|
||||||
value = self.func(inst)
|
value = self.func(inst)
|
||||||
inst.__dict__[self.func.__name__] = value
|
inst.__dict__[self.func.__name__] = value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# Read & write properties
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 by Philipp "philiKON" von Weitershausen
|
||||||
|
# philikon@philikon.de
|
||||||
|
#
|
||||||
|
# Freely distributable under the terms of the Zope Public License, v2.1.
|
||||||
|
#
|
||||||
|
# See rwproperty.txt for detailed explanations
|
||||||
|
#
|
||||||
|
import sys
|
||||||
|
|
||||||
|
__all__ = ['getproperty', 'setproperty', 'delproperty']
|
||||||
|
|
||||||
|
class rwproperty(object):
|
||||||
|
|
||||||
|
def __new__(cls, func):
|
||||||
|
name = func.__name__
|
||||||
|
|
||||||
|
# ugly, but common hack
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
locals = frame.f_locals
|
||||||
|
|
||||||
|
if name not in locals:
|
||||||
|
return cls.createProperty(func)
|
||||||
|
|
||||||
|
oldprop = locals[name]
|
||||||
|
if isinstance(oldprop, property):
|
||||||
|
return cls.enhanceProperty(oldprop, func)
|
||||||
|
|
||||||
|
raise TypeError("read & write properties cannot be mixed with "
|
||||||
|
"other attributes except regular property objects.")
|
||||||
|
|
||||||
|
# this might not be particularly elegant, but it's easy on the eyes
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createProperty(func):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enhanceProperty(oldprop, func):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class getproperty(rwproperty):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createProperty(func):
|
||||||
|
return property(func)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enhanceProperty(oldprop, func):
|
||||||
|
return property(func, oldprop.fset, oldprop.fdel)
|
||||||
|
|
||||||
|
class setproperty(rwproperty):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createProperty(func):
|
||||||
|
return property(None, func)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enhanceProperty(oldprop, func):
|
||||||
|
return property(oldprop.fget, func, oldprop.fdel)
|
||||||
|
|
||||||
|
class delproperty(rwproperty):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createProperty(func):
|
||||||
|
return property(None, None, func)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enhanceProperty(oldprop, func):
|
||||||
|
return property(oldprop.fget, oldprop.fset, func)
|
||||||
|
|
|
@ -7,7 +7,7 @@ $Id$
|
||||||
lzprop
|
lzprop
|
||||||
======
|
======
|
||||||
|
|
||||||
The `lzprop` decorator allows the declaration of lazy properties - attributes
|
The ``@lzprop`` decorator allows the declaration of lazy properties - attributes
|
||||||
that are calculated only on first access, and then just once. This is
|
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.
|
extremely useful when working with objects of a limited lifetime (e.g.
|
||||||
adapters) that provide results from expensive calculations.
|
adapters) that provide results from expensive calculations.
|
||||||
|
@ -52,3 +52,102 @@ already during compilation or object creation):
|
||||||
>>> demo2.value
|
>>> demo2.value
|
||||||
105
|
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.
|
||||||
|
|
Loading…
Add table
Reference in a new issue