Beware of Property Descriptor and __getattr__
Friday, 17 November 2006
What’s wrong with this code?
class C:
def __init__(self, id):
self.id = id
def getdata(self):
if not hasattr(self, '_data')
temp = longComputation()
self._data = temp.value()
return self._data
def __getattr__(self, name):
return databaseLookup(name, 'ID=%s' % self.id)
data = property(getdata)
We were getting intermittent errors where
c = C(82201) print c.data ... SQL error "column data does not exist"
It turns out that longComputation() had a bug, and returned a None sometimes. This caused temp.value() to fail with AttributeError.
The AttributeError caused python to fallback to __getattr__ , which uses the database lookup routine, which tried to look up a column which does not exist.
The moral of the story?
surround property accessors with try…except AttributeError, log the error and raise it as a different kind of exception.
Update 20-Nov-2006: Fixed up the syntax error. Thanks Ben.
Link Exchanges:
Just Web Design. Ford automotive panel van utility trucks
No. 1 — November 18th, 2006 at 5:25 am
I’m a little confused how the code you pasted even works. A getter for a property doesn’t take an argument, and when I paste the code you have into an interpreter, sure enough I get:
>>> print c.data
Traceback (most recent call last):
File “”, line 1, in ?
TypeError: getdata() takes exactly 2 arguments (1 given)
Beyond that though is another problem, your getattr method is called by hasattr itself, so it will never actually execute your block since hasattr(self, ‘_data’) will call __getattr__ with ‘_data’ as the name, which then causes the db lookup which failed.
Generally if I have a getattr and I’m looking to see if something is present, I use the underlying __dict__, so for the getdata function:
def getdata(self):
if ‘_data’ not in self.__dict__:
Would do the trick and avoid calling the getattr. Plus, you avoid accidentally having hasattr call your getattr override.
Cheers,
Ben
No. 2 — November 20th, 2006 at 11:14 pm
Thank Ben, I was working off memory, plus the original code was really ugly.