At EuroPython, Guido made us vote on the Python decorator syntax. I voted against what became the current syntax at the time. Today I rather like it, not only because other languages also use it. I think it doesn’t read as badly as I anticipiated. It especially makes things like dynamic properties much more readable:
>>> class JamesBrown(object):
… @property
… def feel(self):
… return self._feel
There’s one wart, though. There’s no easy syntax for writable (and delable) properties. The traditional way would be this:
>>> class JamesBrown(object):
… def _getFeel(self):
… return self._feel
… def _setFeel(self, feel):
… self._feel = feel
… feel = property(_getFeel, _setFeel)
This isn’t even half as nice to read as the decorator spelling from above. It also leaves setters and getters behind in the class which creates unnecessary clutter.
Of course, there’s a way to use a decorator if you really want:
>>> class JamesBrown(object):
… @apply
… def feel():
… def get(self):
… return self._feel
… def set(self, feel):
… self._feel = feel
… return property(get, set)
Sure, this doesn’t leave setters and getters behind and it is more compact than the previous example. However, apart from the fact that I find this just plain sick :), it still doesn’t read top-down. The fact that a property is created occurs on the last line of the whole beast.
Read & write properties
So, I was looking for a solution and came up with something a few weeks before PyCON 06 called read & write properties. This is what it looks like:
>>> from rwproperty import getproperty, setproperty
>>> class JamesBrown(object):
… @getproperty
… def feel(self):
… return self._feel
… @setproperty
… def feel(self, feel):
… self._feel = feel
Of course, there’s also @delproperty to provide deleters. The order in which all of these are defined doesn’t matter. Internally, they simply return property objects based on already defined property objects. They do that with the sys._getframe(1) hack, a popular but sometimes frowned-upon hack…
All in all, it’s no rocket science (61 LOC + 140 lines of doctest), after all, it’s about cosmetics anyways. I was rather satisfied with my creation and prepared a lightning talk for PyCON 06. I didn’t give it in front of the large crowd because I decided to talk about internet censorship in China instead.
Class properties
I did manage to give my lightning talk some time during the sprints after the conference, though. Jim Fulton and Ian Bicking were around so we were goofing around with even different syntaxes. One particular thing that just popped up (I absolutely have no idea who’s idea it was exactly) was a syntax like this:
>>> from classproperty import classproperty
>>> class JamesBrown(object):
… class feel(classproperty):
… def __get__(self):
… return self._feel
… def __set__(self, feel):
… self._feel = feel
That felt very Pythonic. And hey, it didn’t even need any decorators at all! That isn’t to say that this always is an advantage, but it’s sure nice to note.
I think Ian even quickly hacked this together while the rest of us had already turned to Shiner Bock and whatever beer we had brought with us from the liquor store. Today I gave it a shot. As you can imagine, this implementation is even shorter, thanks to metaclasses (15 LOC, 101 lines of doctest).
Conclusion
I like both of my inventions, but for some reason I prefer the latter one. Class properties feel more Pythonic, I guess because you actually end up spelling out the property API (__get__, __set__). There’s also less to import. Of course, it’d be nice if you wouldn’t have to import it at all… PEP4000, here we come!
Now seriously, it seems almost like waste to spend a whole blog entry babbling about something so insignificant. But isn’t that something we like about Python so much: that we actually spend some time thinking about making solutions pretty and aesthetic?
Leave a Reply