tags:

views:

890

answers:

3

We have a database library in C# that we can use like this:

DatabaseConnection conn = DatabaseConnection.FromConnectionString("...");

this library hides many of the differences between different database engines, like sql function names, parameter names and specifications, etc.

Internally, the DatabaseConnection class is an abstract class implementing some of the basic methods, but the .FromConnectionString method runs through a list of registered specialized types that handles the actual differences, and constructs an object of the right class. In other words, I don't get a DatabaseConnection object back, I get a MSSQLDatabaseConnection or OracleDatabaseConnection object back instead, which of course inherit from DatabaseConnection.

The connection string contains information about what kind of database engine and version this connection is for.

I'd like to create a similar library in python.

Is the right approach to make something that can be constructed like this:

conn = DatabaseConnection("...")

or through a class method:

conn = DatabaseConnection.FromConnectionString("...")

is the first even possible, that is... constructing an object like this and getting back something else, a specialized object, depending on data in the passed string?

Ok, let me ask a different question... What is the pythonic way of doing this?

I basically want to have the DatabaseConnection base class in Python as well, implementing the common methods, and specialize in derived classes, and have a method or function somewhere that based on the connection string constructs and returns the right type of object.

+4  A: 

This is possible in Python, but is probably not the best way to do it. The class factory pattern is essentially a workaround for languages that don't have first class classes. Since Python does have first class classes, you can store a class in a variable, and use that class directly to create instances. To change what class is created, store a different class in the variable.

For example:

class class1:
  def greet(self):
    print "hi"

class class2:
  def greet(self):
    print "hello"

maker = class1
obj1 = maker()

maker = class2
obj2 = maker()

obj1.greet() # prints "hi"
obj2.greet() # prints "hello"
Glomek
Ok, hmm, not quite what I had in mind, but this gives me an idea on how to implement the "default connection factory", perhaps I'll use this idea even though it didn't fit what I wanted entirely, thanks.
Lasse V. Karlsen
+1  A: 

The first one is absolutely possible, and preferable in my opinion. In python, there's really not a whole lot of magic behind constructors. For all intents and purposes, they're just like any other function. I've used this design pattern a few times to indicate that a class shouldn't be instantiated directly, for example:

def DatabaseConnectionFromString(connection_string)
    return _DatabaseConnection(connection_string)

def DatabaseConnectionFromSomethingElse(something_else)
    connection_string = convert_something_else_into_string(something_else)
    return _DatabaseConnection(connection_string)

class _DatabaseConnection(object):
    def __init__(self, connection_string):
        self.connection_string = connection_string

Of course, that's a contrived example, but that should give you a general idea.

EDIT: This is also one of the areas where inheritance isn't quite as frowned upon in python as well. You can also do this:

DatabaseConnection(object):
    def __init__(self, connection_string):
        self.connection_string = connection_string

DatabaseConnectionFromSomethingElse(object)
    def __init__(self, something_else):
        self.connection_string = convert_something_else_into_string(something_else)

Sorry that's so verbose, but I wanted to make it clear.

Jason Baker
Thanks, while both yours and Glomeks answers answer my question in a way, I don't quite like having to rename the base class like that
Lasse V. Karlsen
Funny that you posted that while I made an edit that doesn't require naming the class. If that doesn't work, let me know and I'm sure I can help you with something else.
Jason Baker
+3  A: 

Python doesn't care why type you return.

def DatabaseConnection( str ):   
    if ( IsOracle( str ) ):   
        return OracleConnection( str )  
    else: 
        return SomeOtherConnection( str )
Sanjaya R
+1: I was in the middle of typing almost exactly that!
S.Lott