在 Python 中设计与多个数据库的连接

Design a connection to multiple databases in Python

我有一个 Python 应用程序,它同时使用 SQLite 和 Postgresql。每个数据库都有一个连接器 class:

 class PostgresqlDatabase(Database):
     ...


 class SQLite(Database):
     ...

两者 class 共享相同的方法和逻辑,唯一区别于 SQL 的是 SQL 查询的参数化。大多数 SQL 查询甚至是相同的,例如两者都有一个名为 _get_tag:

的方法
 # postgresql method with %s
 def _get_tag(self, tagcipher):
    sql_search = "SELECT ID FROM TAG WHERE DATA = %s"
    self._cur.execute(sql_search, ([tagcipher]))
    rv = self._cur.fetchone()
    return rv

# sqlite method with ?
def _get_tag(self, tagcipher):
    sql_search = "SELECT ID FROM TAG WHERE DATA = ?"
    self._cur.execute(sql_search, ([tagcipher]))
    rv = self._cur.fetchone()
    return rv

为了真正明确起见,classes 具有完全相同的方法名称。 SQL 查询在每种方法中都不同。那么我的问题是什么?

我发现同时维护这两个 class 很烦人,而且我觉得一个共同的 class 会使长 运行 中的代码受益。 但是,创建一个通用的 class 会创建一个复杂的代码。 __init__ 可能必须初始化正确的底层游标。这将产生一个小的起始开销,并且如果我每次都会查找正确的字符串,例如,小的性能损失,例如

 @property: 
 def sql_search(self):
     return "SELECT ID FROM TAG WHERE DATA = {}".format(
          '?' if self.db == 'SQLite' else '%s')


 def _get_tag(self, tagcipher):

    self._cur.execute(self.sql_search, ([tagcipher]))
    rv = self._cur.fetchone()
    return rv

我也担心这种方法初看时也比较难理解。

离开我个人的例子,我想知道这里最能接受的方式是什么。 我应该继续维护这两个 class 还是写一个更复杂的 class 来完成这一切?

是否有一般的经验法则?

看来你要的是继承。这是 [OOP][1] 的一个关键特性(Another one in Java,是的 Java,但我喜欢他们的文档)。

正如thefourtheye 在评论中所说,我认为您应该将相同的方法移动到一个class(换句话说,删除一组相同的方法)。

这是一个非常简单的例子:

class Connector(Database):
    """This is a super class, common elements go here"""
    def __init__(self):
        self.sql_search = "SELECT ID FROM TAG WHERE DATA = %s"
        self.common_varialbe = None #placeholder
        Database.__init__(self) #add necessary arguments

    def _get_tag(self, tagcipher, wildcard):
        #replace the sql search string with the wildcard.
        self._cur.execute(self.sql_search % (wildcard) , ([tagcipher]))
        rv = self._cur.fetchone()
        return rv
   def some_common_method(self, uncommon_value):
       self.common_variable = uncommon_value

class Postgresql(Connector):
    """postgresql subclass using %s. 
    unique postgresql elements go here"""
    def __init__(self):
        #initialise the superclass
        Connector.__init__(self)

        self.wildcard = '%s'
        self.uncommon_value = 'py hole'
        #other unique values go here

class Sqlite(Connector):
    """etc"""
    def __init__(self):
        #initialise the superclass
        Connector.__init__(self)

        self.wildcard = '?'
        #other unique values go here

    #other methods

即使从这个例子中你也可以看到一些冗余,但包含在其中是为了展示在必要时如何拆分。有了这个 class,我可以:

>>>import connector
>>>sqlite = connector.Sqlite()
>>>sqlite.wilcard
`?`
>>>sqlite.sql_search
`SELECT ID FROM TAG WHERE DATA = %s`
>>>sqlite.sql_search % sqlite.wildcard
`SELECT ID FROM TAG WHERE DATA = ?`

如果它们真的只是字符串不同,则只需要一个子class。您可以使用 dict()s 来存储唯一位:

class Connector(Database):
    def __init__(self,type):
        #describe all types in this dict
        types = {"sqlite":"?",
                 "postgre":"%s"}

        #Database.__init__(self) as necessary
        self.sql_search = "SELECT ID FROM TAG WHERE DATA = %s" % types[type]

    def _get_tag(self, tagcipher):
        #replace the sql search string with the wildcard.
        self._cur.execute(self.sql_search, ([tagcipher]))
        rv = self._cur.fetchone()
        return rv

因此 class:

>>>c = connector.Connector('sqlite')
>>>c.sql_search
`SELECT ID FROM TAG WHERE  DATA = ?`

只要它们正确地继承自 Database superclass,subclasses 将在调用 Database.__init__(*args) 时共享其游标