Wednesday, January 28, 2009

Canonical Python __str__ method

Here's a python class that shows some approaches for building up a meaningful __str__() to display the class's data attributes (like overriding toString() in java). The first way is to show a specific subset (or ordering) of attributes. The second way (currently commented out) just shows all the classes attributessorted by name. I also show how to make it work with a subclass--either leveraging the superclass __str__() method or doing it all from within the subclass. Of course, it should be easy to adjust the output to your own needs (padding, etc).

Nothing earth-shattering here, but it can save some work if it's a big class with lots of attributes. The example should work with modern CPython, including Python 3:

class X:
def __init__(self):
self.a = "apple"
self.b = "banana"
self.c = "cranberry"

def __str__(self):

# to show specific variables
showList = ["a", "b"]

# to show include all variables in sorted order
#showList = sorted(set(self.__dict__))

return ("X(%i):\n" % id(self)) + "\n".join([" %s: %s" % (key.rjust(8), self.__dict__[key]) for key in showList])

class Y(X):

def __init__(self):
X.__init__(self)
self.d = "date"
self.e = "elderberry"

def __str__(self):
showList = ["a","d","e"]

# if you want to use the superclass as-is and supplement from this subclass
# return X.__str__(self) + "\n Y:\n" + "\n".join([" %s: %s" % (key.rjust(8), self.__dict__[key]) for key in showList])

# if you want to do everything from this subclass
return ("Y(%i):\n" % id(self)) + "\n".join([" %s: %s" % (key.rjust(8), self.__dict__[key]) for key in showList])

print(Y())

This outputs:

Y(12172528):
a: apple
d: date
e: elderberry


Okay, but maybe this can just be a helper method of some kind. The class name info can be pulled of of the self.__class__ object, and then the function can be more generic:

def str_impl(anInstance, displayAttributeList):

classLine = "%s.%s(%i):\n" % (anInstance.__class__.__module__, anInstance.__class__.__name__, id(anInstance))
return (classLine + "\n".join([" %s: %s" % (key.rjust(8), anInstance.__dict__[key]) for key in displayAttributeList]))

class XX:

showList = ["a", "b"]

def __init__(self):
self.a = "apple"
self.b = "banana"
self.c = "cranberry"

def __str__(self):
return str_impl(self, self.__class__.showList)


class YY(XX):
showList = ["a","d","e"]

def __init__(self):
X.__init__(self)
self.d = "date"
self.e = "elderberry"

This outputs:

__main__.YY(12172784):
a: apple
d: date
e: elderberry



Note that __main__ is the module since the classes are defined in the "main" scope. The correct module/package would print if I had used one. Again, you wouldn't even need to bother with this for a small class--but for a class with a lot of attributes (which might be a sign it's time to break it up anyway ;-), it saves some tedium.

Friday, January 9, 2009

Connecting to an Oracle Database from ASP.Net/SharePoint

So you’re writing your “Hello Oracle Database” from ASP.Net/SharePoint, and you get this error: “System.Data.OracleClient Requires Oracle Client version 8.1.7 or greater”.

Perhaps you don’t really want to install the 475 Mb Oracle Client on your system just to talk to a database. Some quick googling suggests you can just install the Oracle Instant Client, and if you don’t care about internationalization, you can even go with the Instant Client “Basic Lite” which gets you down to about 18 Mb:

http://www.oracle.com/technology/software/tech/oci/instantclient/htdocs/winsoft.html [32 bit version]

Yeah, it’s an order of magnitude larger than the jdbc jar you’d use if you were connecting from Java, but at least it’s not a half Gig. But what if you don’t even want to install it? This blog entry shows the 4 files you’ll need to from the instant client:

http://www.thewayofcoding.com/2008/02/c-net-programming-tip-connecting-to-an-oracle-database/

In my case, I’m actually developing a SharePoint web part. Therefore, I’d like to make the set of driver files universally available for anything deployed on the system. So I drop those four files:
· oci.dll
· orannzsbb10.dll
· oraocci10.dll
· oraociicus10.dll

into the system driver directory (in this case C:\Windows\System32). And now I’m good to go.
Note: I’m not sure if it’s related, but I did not have to change any permissions as seems to happen when you actually install the client (at least in some cases)—as described here:

http://moustafa-arafa.blogspot.com/2007/04/systemdataoracleclient-requires-oracle.html

I suppose it’s time I update my About Me to say I do .Net now too :-)