How can I efficiently in a proper pythonic pool/cache objects holding an expensive answer from the database?
My module contains following schematic code:
import sqlite3
db = sqlite3.connect(config.DB) # application shared database
db.row_factory = sqlite3.Row
class Foo:
def __init__(self, id):
self.id = id
@property
def bar(self):
if not hasattr(self, '_bar'):
row = db.execute(
'SELECT bar FROM bars WHERE id=?', (self.id, )).fetchone()
self._bar = row['bar'] if row else None
return self._bar
Application is creating over its lifetime Foo objects to access some values bar in the database. The Foo.bar property represents an expensive operation, therefore the lazy processing. Instances of Foo class are not changing much at all, they are just a read-proxies for database stored data.
Now I realized, that Foo instances with the same id are created and destroyed quite often. Every time the object dies, the knowledge of Foo.bar is lost and has to be fetched from the database later again.
I figured two ways how I could deal with it, either pooling objects themselves:
class DBObject:
_pool = {}
def __new__(cls, *params, **key_params):
key = cls.__name__ + str(params) + str(key_params)
if not key in DBObject._pool:
self = object.__new__(cls, *params, **key_params)
DBObject._pool[key] = self
self = DBObject._pool[key]
return self
class Foo(DBObject):
...
or their states:
class DBObject:
_states = {}
def __new__(cls, *params, **key_params):
self = object.__new__(cls, *params, **key_params)
key = cls.__name__ + str(params) + str(key_params)
if not key in DBObject._states:
DBObject._states[key] = {}
self.__dict__ = DBObject._states[key]
return self
class Foo(DBObject):
...
Is there some better and more pythonic solution? Did I oversee something?
PS: I am Python beginner with Java/C++ background.
I’d use a simple memoize decorator, here is a generic one:
You can use it to decorate the
Fooclass:You can also apply the
memoizedecorator to thebarmethod, and clean it up a bit:Also new in Python3.2 is
functools.lru_cachewhich has a number of nice features (clearing caches, keeping track of hit/miss stats, optional size and type checking)