I've often been frustrated by the inability of the built-in property descriptor to handle anything other than a read-only property when used as a decorator. Furthermore, read/write/delete properties take their doc-string and property definition at a non-intuitive and awkward place (after the getter/setter/delter functions). The following are three possible solutions to this problem (inspired by message http://groups.google.com/group/comp.lang.python/msg/9a56da7ca8ceb7c7). I don't like the solution in that thread because it uses apply() which will go away in Python 3.
Solution 1: new built-in function/descriptor def prop(func): funcs = dict(enumerate(func())) return property(funcs[0], funcs[1], funcs.get(2), func.__doc__) class Test(object): @prop def test(): """test doc string""" def fget(self): return self._test def fset(self, value): self._test = value def fdel(self): del self._test return fget, fset, fdel Of course the name (prop) could be changed... I couldn't think of anything more concise. Pros: (1) encapsulation of property logic inside function namespace, preventing clutter in class namespace. (2) doc string appears in a more natural place, before getter/setter/ delter logic, as in classes and functions. Cons: (1) additional built-in name. (2) duplication/boilerplate in return line (DRY violation). Solution 2: Enhance the built-in property descriptor, so if it receives a generator function taking no arguments as its first and only argument it treats the generated result similar to the above prop function. Example: import types def property_(fget, fset=None, fdel=None, doc=None): if fget is not None and fset is None and fdel is None and doc is None \ and fget.func_code.co_argcount == 0: # is there a way to detect a generator function without calling it? gen = fget() if isinstance(gen, types.GeneratorType): result = list(gen) assert len(result) == 1, "<oops! generated wrong number of results>" funcs = dict(enumerate(result[0])) doc = fget.__doc__ fget = funcs[0] fset = funcs[1] fdel = funcs.get(2) # normal property logic follows return property(fget, fset, fdel, doc) class Test(object): @property_ def test(): """test doc string""" def fget(self): return self._test def fset(self, value): self._test = value def fdel(self): del self._test yield fget, fset, fdel Pros: (1) overloaded use of existing property descriptor prevents built-in clutter. (2) encapsulation of property logic inside function namespace, preventing clutter in class namespace. (3) doc string appears in a more natural place, before getter/setter/ delter logic, as in classes and functions. Cons: (1) possible unexpected behavior when zero-argument function (fget) is called. (2) duplication/boilerplate in yield line (DRY violation). It would take two "errors" to get the "unexpected" behavior in this second solution. First, property getters normally (always?) take a 'self' argument. Second, the function must be a generator yielding a single result (not a common type of property getter). It would be best to have some way of knowing if the function is in fact a generator before calling it since that would result in the least amount of surprise. Solution 3: Possibly the most controversial, but arguably the most elegant solution: class Test(object): @property def test(): """test doc string""" yield(self): return self._test yield(self, value): self._test = value yield(self): del self._test This option would require new syntax for the yield keyword, which would yield an unnamed function object (similar to a lambda object). The important part is that the no-argument generator function passed to property would yield up to three callables, which would be used in order as the getter/setter/delter for the property. The doc string for the property would be the doc string from the generator function. An implementation of the additional property logic needed for this solution is postponed until there is sufficient interest. Pros: (1) overloaded use of existing property descriptor prevents built-in clutter. (2) encapsulation of property logic inside function namespace, preventing clutter in class namespace. (3) doc string appears in a more natural place, before getter/setter/ delter logic, as in classes and functions. Cons: difficult to implement? Of course, more error checking would be necessary if any these enhancements were added to the standard library. Thoughts? ~ Daniel -- http://mail.python.org/mailman/listinfo/python-list