Taking a short break from my artwork craze (but I promise more is to come), I reviewed some of the new features and changes in Python, brought by versions 2.6 and 3.0. There are many interesting features, but a very specific one caught my eye: the ability to modify existing properties.
From the documentation:
Properties now have three attributes, getter, setter and deleter, that are decorators providing useful shortcuts for adding a getter, setter or deleter function to an existing property. You would use them like this:
class C(object): @property def x(self): return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x class D(C): @C.x.getter def x(self): return self._x * 2 @x.setter def x(self, value): self._x = value / 2
I found this very interesting. If you're, for some reason, a frequent reader of this blog, you'd remember how in a previous post I suggested a hack to allow namespaces inside Python classes. However, it was largely incomplete because I couldn't get it to work for properties. Perhaps now I can?
Now, don't let that pretty decorator syntax mislead you. Doing:
@x.setter
def x(self, value):
self._x = value
is exactly identical to doing:
def x_set(self, value): # Name changed to preserve 'x'
self._x = value
x = x.setter(x_set)
And this is exactly what I was missing to complete my code.
So here is the new make_namespace, applying also to properties:
(This was only tested on Python 3, but should also work for Python 2.6. It will not work on version 2.5 or lower)
import types
def make_namespace(self, ns_cls):
"This code iterates all functions and properties in ns_cls and binds them to self"
for attr_name in dir(ns_cls):
attr = getattr(ns_cls, attr_name)
if type(attr) == types.FunctionType:
setattr(ns_cls, attr_name, attr.__get__(self) )
elif type(attr) == property:
setattr( ns_cls, attr_name, attr
.getter( lambda x : attr.fget(self))
.setter( lambda x,v: attr.fset(self,v))
.deleter(lambda x : attr.fdel(self))
)
return ns_cls()
Notice that I can chain getter, setter, and deleter, because they all return a property object.
And here is a possible usage:
class YetAnotherGuiWindow:
def __init__(self, pos, size, ...):
...
self._size = size
self.spatial = make_namespace(self, self.spatial)
class spatial:
...
def _get_size(self):
return self._size
def _set_size(self, size):
self._size = size
size = property(_get_size, _set_size)
...
...
# Meanwhile, in my console
>>> g.spatial.size = (100,100)
>>> g.spatial.size
(100, 100)
>>> g.spatial.size = (140,10)
>>> g.spatial.size
(140, 10)
>>> g._size
(140, 10)
I hope you'll find this useful!