Class properties
================

:Author:   Philipp von Weitershausen
:Email:    philikon@philikon.de
:License:  Zope Public License, v2.1

Motivation
----------

Using method decorators and descriptors like ``property``, we can
easily create computed attributes:

  >>> class JamesBrown(object):
  ...     @property
  ...     def feel(self):
  ...         return self._feel

An attribute like this cannot be written, though.  You would have to
do something like this:

  >>> class JamesBrown(object):
  ...     def _getFeel(self):
  ...         return self._feel
  ...     def _setFeel(self, feel):
  ...         self._feel = feel
  ...     feel = property(_getFeel, _setFeel)

The problem with this approach is that it leaves the getter and setter
sitting around in the class namespace.  It also lacks the compact
spelling of a decorator solution.  To cope with that, some people like
to write:

  >>> class JamesBrown(object):
  ...     @apply
  ...     def feel():
  ...         def get(self):
  ...             return self._feel
  ...         def set(self, feel):
  ...             self._feel = feel
  ...         return property(get, set)

This spelling feels rather cumbersome, apart from the fact that
``apply`` is `going to go away`_ in Python 3000.

.. _going to go away: http://www.python.org/peps/pep-3000.html#id24


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.


Class properties
----------------

Class properties let you define properties via the ``class``
statement.  You define a dynamic property as if you were implementing
a class.  This works like this:

  >>> from classproperty import classproperty
  >>> class JamesBrown(object):
  ...     class feel(classproperty):
  ...         def __get__(self):
  ...             return self._feel
  ...         def __set__(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'

Of course, deleters are also possible:

  >>> class JamesBrown(object):
  ...     class feel(classproperty):
  ...         def __get__(self):
  ...             return self._feel
  ...         def __set__(self, feel):
  ...             self._feel = feel
  ...         def __delete__(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'
