This recipe suggests an idiom for property creation that avoids cluttering the class space with get/set/del methods that will not be used directly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | """
Rather than defining your get/set/del methods at the class level, as is usually done, e.g.
class MyClass(object):
def __init__(self):
self._foo = "foo"
def getfoo(self):
return self._foo
def setfoo(self, value):
self._foo = value
def delfoo(self):
del self._foo
foo = property(getfoo, setfoo, delfoo, "property foo's doc string")
I would like to suggest the following alternative idiom:
"""
class MyClass(object):
def __init__(self):
self._foo = "foo"
self._bar = "bar"
def foo():
doc = "property foo's doc string"
def fget(self):
return self._foo
def fset(self, value):
self._foo = value
def fdel(self):
del self._foo
return locals() # credit: David Niergarth
foo = property(**foo())
def bar():
doc = "bar is readonly"
def fget(self):
return self._bar
return locals()
bar = property(**bar())
|
Using the standard idiom to create properties unnecessarily clutters the class space with get/set/del methods that will not be called directly by users of your class (although, of course, they could be). Using the above idiom removes the get/set/del methods from the class space by nesting them inside of a function with the same name as the property you will create. This function is then used to create your new property, at which time said function will no longer have any referrers. As such, it will not be accessible to users of your class - only the newly created property will remain accessible.
Following this idiom is, of course, unnecessary as the standard method works just fine. And, in fact, using this idiom for creating properties like bar (above), will seem excessive. Still, it does provide a clean method for restricting the avenues of access to your classes by their users - if there are no get/set/del methods available, then users of your classes will have to use your property, which was your intention.
NOTE: see comment "Just so users are aware" below.
Nice use of an enclosing namespace.
A variation. property() accepts the keyword arguments fget, fset, fdel, and doc. A variation would be to use those names and return locals() rather an explicit tuple. For example,
Returning locals() does seem to violate "explicit is better than implicit" but it simplifies the return value a little for read-only properties. Here's the read-only property example:
Just so users are aware. Using locals() is a clean solution, however, it must be used with care. The nested method names must be as David has prescribed, and you may not introduce any other names into the local scope, e.g.
def bar():
bar = property(**bar())
will give the following error:
TypeError: 'x' is an invalid keyword argument for this function
My idiom does not suffer from this restriction, however, you are required to return the get/set/del/doc in the correct order, and to not return anything other than those items.
A possible compromise would be to return an explicit dictionary rather than a tuple or locals(), e.g.
requires python 2.3+
return dict(fget=fget, doc=doc)
using a metaclass? If one has a lot of getter/setter methods, using a metaclass approach might be easier in the long run. Maybe something like the following. I have to admit though, that the code for the meta class is much more complicated than the original recipe.
The solution you've provided allows for the automated creation of simple properties. But that is not the issue being addressed here. While the examples provided above show only simple properties, the intended use for this idiom is to encapsulate the get/set/del methods of more complex properties, such as the following:
suppose we're inside a class, and we want to make
a read-only property 'average', that returns the
average fitness of all individuals in a population
def average():
average = property(**average())
The idea is not about avoiding having to create the get/set/del methods yourself; rather it is about avoiding leaving those methods accessible to users of your class by removing those methods from the class space (where they are normally defined). For simple property creation alone, I would refer people to my own makeproperty recipe, or to your recipe using metaclasses. For a more related metaclass solution to this problem I would refer people to the following attempt:
http://aspn.activestate.com/ASPN/Mail/Message/python-list/1660895
You are right, of course. I guess I got carried away a bit and solved a problem that was not asked for :-)
Nice, indeed.
It seems you can use 'del' This appears to work:
Yes, you can certainly use del to explicitly remove the get/set/del methods from the class' namespace (note: the parenthesis are not required). This idiom does that implicitly for you. There are a couple of other things it does for you:
It groups the methods together visually through indentation, offsetting them from other method definitions, and tying them more directly to the property definition.
It allows for a consistent and repeatable naming convention. The names of the get/set/del methods are, or can be, identical for every property. There's no need to have get_foo, set_foo, get_bar, set_bar for properties foo and bar when you can just use fget, fset for both.
Whether these provide any real benefit can be debated. Personally, I like grouping related things together, and seperating them out from unrelated things. And I like having every property definition look nearly identical, by being able to re-use the names fget, fset, fdel for each one. The fact that these auxiliary methods are also automatically removed from the class' namespace, reducing namespace pollution and unintended avenues for access, is a nice side benefit.
I like the idea to use enclosed namespace. Here is another solution:
Now property definition is very simple:
Unnecessary self argument can be avoided by automatially aplying staticmethod decorator in metaclass.
Possible idiom after PEP318. There is ongoing discussion on python-dev about accepting PEP318 - Decorators for Functions, Methods and Classes - for inclusion into Python 2.4. If accepted, some small changes to the property descriptor would allow this idiom to be re-expressed as follows:
This is better, but still not ideal: it's still a function definition masquerading as a property definition; and it requires you to return locals() to gain access to the get/set/del methods. Perhaps, one day, the language will grow a property block, e.g.
As an aside: Ruby's approach is interesting, though not applicable
Please check out my recipe:
Easy Property Creation in Python
http://code.activestate.com/recipes/576742/
Only 7 lines of code, easy to use, easy to understand, easy to customize, make the code much netter and will save you a lot of typing.