Recipe6.8.Avoiding Boilerplate Accessors for Properties


Recipe 6.8. Avoiding Boilerplate Accessors for Properties

Credit: Yakov Markovitch

Problem

Your classes use some property instances where either the getter or the setter is just boilerplate code to fetch or set an instance attribute. You would prefer to just specify the attribute name, instead of writing boilerplate code.

Solution

You need a factory function that catches the cases in which either the getter or the setter argument is a string, and wraps the appropriate argument into a function, then delegates the rest of the work to Python's built-in property:

def xproperty(fget, fset, fdel=None, doc=None):     if isinstance(fget, str):         attr_name = fget         def fget(obj): return getattr(obj, attr_name)     elif isinstance(fset, str):         attr_name = fset         def fset(obj, val): setattr(obj, attr_name, val)     else:         raise TypeError, 'either fget or fset must be a str'     return property(fget, fset, fdel, doc)

Discussion

Python's built-in property is very useful, but it presents one minor annoyance (it may be easier to see as an annoyance for programmers with experience in Delphi). It often happens that you want to have both a setter and a "getter", but only one of them actually needs to execute any significant code; the other one simply needs to read or write an instance attribute. In that case, property still requires two functions as its arguments. One of the functions will then be just "boilerplate code" (i.e., repetitious plumbing code that is boring, and often voluminous, and thus a likely home for bugs).

For example, consider:

class Lower(object):     def _ _init_ _(self, s=''):         self.s = s     def _getS(self):         return self._s     def _setS(self, s):         self._s = s.lower( )     s = property(_getS, _setS)

Method _getS is just boilerplate, yet you have to code it because you need to pass it to property. Using this recipe, you can make your code a little bit simpler, without changing the code's meaning:

class Lower(object):     def _ _init_ _(self, s=''):         self.s = s     def _setS(self, s):         self._s = s.lower( )     s = xproperty('_s', _setS)

The simplification doesn't look like much in one small example, but, applied widely all over your code, it can in fact help quite a bit.

The implementation of factory function xproperty in this recipe's Solution is rather rigidly coded: it requires you to pass both fget and fset, and exactly one of them must be a string. No use case requires that both be strings; when neither is a string, or when you want to have just one of the two accessors, you can (and should) use the built-in property directly. It is better, therefore, to have xproperty check that it is being used accurately, considering that such checks remove no useful functionality and impose no substantial performance penalty either.

See Also

Library Reference and Python in a Nutshell documentation on the built-in property.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net