Sqlalchemy顶部的抽象层

时间:2018-03-20 03:08:43

标签: python python-3.x sqlalchemy

我正在使用Sqlalchemy的Python 3.6项目,我们希望在Sqlalchemy上有另一层抽象。如果需要,这将允许我们更容易地用另一个库替换Sqlalchemy。

在此示例中,它是DbHelper类:

dbhelper.py

from dbconn import dbconn
from models.animals import Dogs

class DbHelper():
    @staticmethod
    def get_dog_by_nickname(nickname):
        return dbconn.session.query(Dogs).get(nickname)

main.py

from dbhelper import DbHelper

class Dog():
    def __init__(self, nickname, breed, age):
        self.nickname = nickname
        self.breed = breed
        self.age = age

    @classmethod
    def using_nickname(cls, nickname):
        row = DbHelper.get_dog_by_nickname(nickname)
        return Dog(row.id, row.breed, row.age)

dog = Dog.using_nickname('Tom')

问题:有没有比创建DbHelper类更好的方法用作容器并且只有staticmethod?我读过这不是pythonic。

staticmethod中的所有dbhelper.py函数转换为常规方法将在我们from dbhelper import *

时填充命名空间

1 个答案:

答案 0 :(得分:4)

是的,有一个更好的解决方案,而不是创建一个充满staticmethod的类:只是不创建类,并使它们成为所有模块级函数:

from models.animals import Dogs

dbconn = ... 

def get_dog_by_nickname(nickname):
    return dbconn.session.query(Dogs).get(nickname)

充满staticmethod的类的唯一真正意义是为所有这些函数提供一个命名空间。已经的模块是所有这些函数的命名空间因为它更直接地受语言语法的支持,这意味着当你想明确地这样做时,你可以更容易地绕过命名空间(例如,from dbhelper import ...)。

你说:

  

将dbhelper.py中的所有staticmethod函数转换为常规方法将在我们from dbhelper import *

时填充命名空间

但答案显而易见:不要from dbhelper import *,而只是import dbhelper。然后,您使用DbHelper.spam()撰写的所有代码都将变为dbhelper.spam()

如果你真的想要两个级别的命名空间,你可以只使用带有子模块的包,而不是带有类的模块。但我无法在这里看到你需要两级命名空间的充分理由。

另一个替代方案(如评论中juanpa.arrivillaga所建议的)是将其转换为真正的类,其中每个实例(即使实际代码中可能只有一个实例)都有自己的self.dbconn而不是使用全局模块。 dbconn可以传递到__init__,也可以直接在__init__内构建。例如:

class DbHelper:
    def __init__(self, dbname, otherdbparam):
        self.dbconn = dblib.connect(dbname, otherdbparam)
    def get_dog_by_nickname(self, nickname):
        return self.dbconn.session.query(Dogs).get(nickname)

请注意,我们正在使用常规方法,并访问正常的实例变量。这就是一个类 - 将一些状态与转换该状态的方法结合起来。

你如何决定两者之间?好吧,如果每个进程只有一个dbconn,那么它们在功能上等同于,但从概念上讲它们有不同的含义。如果您将DbHelper视为数据库,连接和数据库行为,它应该是一个类,您应该实例化该类的实例并以这种方式使用它。如果你认为它只是一堆在具有独立存在的dbconn上运行的辅助函数,它应该是一个扁平模块。

在某些语言(如Java)中,还有一点是使用一个满是staticmethod的类 - 等价物:语言要么不支持"自由函数",要么它们与方法完全不同。但这在Python中并不正确。

虽然我们正在使用它,您是否希望您的模块导出Dogsdbconn作为" public"界面的一部分?如果没有,您应该在模块的顶部添加__all__规范,如下所示:

from models.animals import Dogs

__all__ = [
    'get_dog_by_nickname',
    ...
]

dbconn = ... 

def get_dog_by_nickname(nickname):
    return dbconn.session.query(Dogs).get(nickname)

或者,或者,将所有"私人"带下划线的模块成员:

from models.animals import Dogs as _Dogs

_dbconn = ... 

def get_dog_by_nickname(nickname):
    return _dbconn.session.query(_Dogs).get(nickname)

无论哪种方式,模块的用户仍然可以访问"私有"数据,但它不会显示在from dbhelper import *help(dbhelper),许多IDE中的默认自动完成等等。