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):Method _getS is just
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)
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):The simplification doesn't look like much in one
def _ _init_ _(self, s=''):
self.s = s
def _setS(self, s):
self._s = s.lower( )
s = xproperty('_s', _setS)
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.