Twactor, and some Python descriptor amazingness.
For the past few days, I’ve been working on a fledgling twitter client library for Python. The libraries which currently exist are, not to put too fine a point on it, cop-outs. One overrides __getattribute__ so that you are literally just making API calls directly from Python, and then it deserializes the JSON data it gets back and gives it straight to you. This might work for some, although I prefer a more refined approach.
Anyways, I’m writing this library to take advantage of Python’s descriptors - you should be able to access data via attributes, not just dictionary calls, and it should be presented to you in a nice format, with names converted to more Pythonic equivalents and packaged into neat little namespaces so that it makes sense. I’ve also created a basic framework for incremental cacheing of the data — the library only ever contacts twitter when it needs to. An example of where this is important is the following: when you download info on one tweet, you get a small amount of info on the user who posted it. This info contains stuff like username, full name, profile image URL and the like - basic stuff. Now, a more naïve library might, if you wanted to fetch that user info, download all the info for that user. This library, however, creates a new instance of a ‘User’ class (compare to Django’s models), which contains a cached version of all the info from the tweet. So, if you want to access this User’s username, you can do status.user.username, and you’ll get the username without any additional requests being made. If, however, you want the user’s profile’s background color, more info needs to be fetched, and so status.user.profile.bg_color will go and fetch more info from the server, storing it in the cache. Then, any other information it fetched from the server will be accessible via that cache.
All in all, I think this library will be pretty good when finished. Eventually, I’m hoping to make it a little more than just a twitter client, but more on that later…
So, to the second purpose of this post, I’m going to explain the ‘Python descriptor amazingness…’ to you. Basically, when defining Python descriptors on classes (via property), you often have to write three functions — one for getting the value, one for setting it, and one for deleting it. An example might look something like this:
class SomeClass(object):
def __init__(self, somevalue=None):
self._value = some_value
def _get_value(self):
return self._value
def _set_value(self, value):
self._value = value
def _del_value(self):
self._value = None
value = property(
fget=_get_value,
fset=_set_value,
fdel=_del_value)
On instances of this class, you can access the value attribute to get the value. But the thing is, this quickly clutters up your class’s namespace with _get_*, _set_* and _del_* methods. Well, I’ve figured out a better way of doing this. All we need is a little helper lambda at the top called property_shortcut, which will handle the calling of property:
property_shortcut = lambda x: property(**x())
class SomeClass(object):
def __init__(self, somevalue=None):
self._value = some_value
@property_shortcut
def value():
def fget(self):
return self._value
def fset(self, value):
self._value = value
def fdel(self):
self._value = None
return locals()
And this will work exactly the same as the one before, without cluttering your class’s namespace with junk. There are several things to note here:
- The value method takes no explicit self argument. This allows the method to be called unbound (i.e. without being on an instance). If it did accept self, then this wouldn’t work.
- The names of the functions fget, fset and fdel are the keyword arguments that the property function accepts. This means that calling locals() will return a dictionary with these three names, and that is why property_shortcut works, as it uses Python’s double asterisk method for unpacking a dictionary into keyword arguments.
So that’s how we roll. Hope you like it ;-)
posted 6 months ago | Permatime