יום ראשון, 15 בנובמבר 2009

Overloading python getattr

Recently I've been trying to figure out how to elegantly polymorph using Python.
I'm writing a little library and I want to hide inside info from the caller - i.e. keep clean API.


Consider a library that drives database servers and suppose to support many kinds of such servers.
The user can ask his program to do some stuff with Oracle and some stuff with MySql


Naturally, the commit or connect methods of Oracle is different then those of MySql. However, the caller(in our case the user program) of such library does not care about it.


Lets look at the caller code:











db_type = get_db_type_from_user
db1 = DB(db_type)
#and a more vague operation
db_type = get_db_type(some_ip_we_got_from_user)
db2 = DB(db_type)
db1.connect()
db2.connect()





Nice and clean right?


But how can this be implemented in the DB library code?
Well, the title is not lying - overloading getattr:










class ORACLE:
    def GetDBType(self):
        return "ORACLE"
class MySql:
    def GetDBType(self):
        return "MySql"
class DB:
    def __init__(self, type):
        if type == "ORACLE":
            self.o = ORACLE()
        if type == "MySql":
            self.o = MySql()
    def __getattr__(self, name):
        return getattr(self.o, name)

All we need is a test program for this:









db1 = DB("Oracle")
db2 = DB("MySql")
print "db1 is a DB of type: ", db1.GetDBType()
print "db2 is a DB of type: ", db2.GetDBType()

Lets run it:










# python polyDB.py
db1 is a DB of type:  ORACLE
db2 is a DB of type:  MySql

Don't you just love python?