Introspection with classes
Kirk McDonald
mooquack at suad.org
Tue Jan 31 08:00:59 EST 2006
Me again! Same project as before, if you're playing along at home.
The Everything Engine model (which I am blatantly copying in my project)
is that everything in the engine is a 'node,' and that every node can be
stored to and read from the database. Since this is the backend for a
website, minimizing hits on the database is a worthy goal. Thus, it is
paramount that reading in any given subclass of the base 'Node' class
take (optimally) one query. (A simple, yet fairly simple-minded design
-- and my first stab at the problem -- would be to have each subclass
read in its own data after calling its parent's -- or parents'! -- read
method(s). This has the disadvantage of cluttering a lot of code with
database queries and making it quite difficult to create new subclasses.)
Here's a rough sketch of what I'm doing at the moment. I've invested
enough time into it at this point that I've totally lost track of
whether it's a good idea, and I would please like someone to tell me.
Every node has certain attributes. In this outline, let's say these are
the node's unique id number, its title, and the id number of its owner.
class Node:
dbInfo = { 'title' : ['node', 'title', ""]
'owner_id' : ['node', 'owner_id', None] }
def __init__(self):
self.default()
def default(self):
self.node_id = None
for k, v in self.dbInfo.items():
setattr(self, k, v[2])
def read(self, db, node_id):
# construct a database query from the table/column info in
# dbInfo
def commit(self, db):
# write the current values to the db
def insert(self, db):
# insert a new node using the current values
def nuke(self, db):
# remove the node from the db, then:
self.default()
So, simple enough. (The real version has signifigantly more stuff going
on, as well as a permission system, but I have elided these for the sake
of clarity.) By doing that dance with dbInfo, I can easily subclass Node.
Say we want a new node that holds some text. We just need to add a new
attribute like so:
class Document(Node):
dbInfo = {}
dbInfo.update(Node.dbInfo)
dbInfo['content'] = ['docs', 'text', ""]
... and that's it. That's the whole Document class. (Well, the real
version has a render method that takes the content, parses it in
interesting ways, and writes it to the client, but that's simple, too.)
The shortness of this class is why this strikes me as a good idea.
The other advantage is I can make use of multiple inheritance. First,
say we have a class that just holds a dictionary:
class DictNode(Node):
dbInfo = {}
dbInfo.update(Node.dbInfo)
dbInfo['stuff'] = ['dictnode', 'dict', {}]
(Assume a mechanism is provided to automatically pickle the dictionary,
which there is.)
Now say I want a nodetype that's just a document with a dictionary
attached. This is almost exactly how the User class is implemented: the
document holds the user's "homenode," which is just a brief bio or
whatever the user wants to put in there, and the dictionary holds the
user's settings. They snap together like Legos. It looks just like this:
class User(Document, DictNode):
dbInfo = {}
dbInfo.update(Document.dbInfo)
dbInfo.update(DictNode.dbInfo)
If the User class wanted its own attributes (say, a password), they
would just get added to the end.
This is my third stab at the problem (the first was mentioned above, the
second was basically the same as this but dbInfo was an instance
variable instead of a class variable which, uh, was sort of dumb).
Coming from C++, I'm still wrapping my brain around Python's
introspection abilties. Can I generalize this more? Am I missing some
idiom or language feature that would benefit me? Where's my time machine?
-Kirk McDonald
More information about the Python-list
mailing list