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!