如何在Python库模块中处理数据库连接


23

我已经在Python中创建了一个库,其中包含用于访问数据库的函数。这是一个围绕第三方应用程序数据库的包装库,由于第三方应用程序未提供像样的API的事实而编写。现在,我最初让每个函数在正常的函数调用期间打开数据库连接,直到我的程序逻辑对函数使用嵌套调用为止,然后我将调用特定函数数千次。这不是很出色。分析这表明开销在数据库连接设置中-每个函数调用一次。因此,我将打开的连接从函数内部移动到模块本身,以便在导入库模块时将打开数据库连接。这给了我令人满意的表现。

现在我有两个问题。首先,我是否需要担心我不再显式关闭数据库连接,并且如何通过此设置显式地进行操作?其次,我所做的工作是否接近良好实践的领域?否则我将如何处理呢?


1
提供一个openConn函数并使用户将其传递给他们调用的每个函数,这样他们就可以在with语句中定义连接的范围或其他内容
Daniel Gratzer 2013年

1
我同意jozfeg的观点,考虑创建一个类,该类在构造函数中打开db连接,并在出口处关闭该连接
Nick Burns 2013年

Answers:


31

这实际上取决于您使用的库。其中一些可能会自行关闭连接(注意:我检查了内置的sqlite3库,但没有)。当对象超出范围时,Python将调用析构函数,并且这些库可能实现一个析构函数,该析构函数优雅地关闭连接。

但是,事实并非如此!与其他人在评论中一样,我建议将其包装在一个对象中。

class MyDB(object):

    def __init__(self):
        self._db_connection = db_module.connect('host', 'user', 'password', 'db')
        self._db_cur = self._db_connection.cursor()

    def query(self, query, params):
        return self._db_cur.execute(query, params)

    def __del__(self):
        self._db_connection.close()

这将在开始时实例化数据库连接,并在实例化对象的位置超出范围时将其关闭。注意:如果您在模块级别实例化对象,它将在整个应用程序中保留。除非有此意图,否则我建议将您的数据库功能与非数据库功能分开。

幸运的是,python已对Database API进行了 标准化,因此它将与您的所有兼容DB一起使用:)


您如何避免这种self情况def query(self,
samayo,2015年

2
定义避免?Self是将其定义为实例方法而不是类方法的东西。我猜您可以将数据库创建为类的静态属性,然后仅使用类方法(在任何地方都不需要自身),但是数据库对于类是全局的,而不仅仅是其单个实例化。
特拉维斯

是的,因为我试图使用您的示例进行一个简单的查询,db.query('SELECT ...', var)并且它抱怨需要第三个参数。
samayo,2015年

@samson,您需要首先实例化MyDB对象:db = MyDB(); db.query('select...', var)
cowbert

这阻止了消息ResourceWarning: unclosed <socket.socket...
Bob Stein

3

在处理数据库连接时,有两件事要注意:

  1. 防止多个连接实例化,让每个函数打开数据库连接被认为是不好的做法,如果数据库会话数有限,则会话用完;至少您的解决方案不会扩展,而是使用单例模式,您的类只会实例化一次,有关此模式的更多信息,请参见链接

  2. 在退出应用程序时关闭连接,假设您没有,并且您至少有十几个正在运行的应用程序实例正在执行相同的操作,起初一切都会好起来,但您将用尽数据库会话,唯一的解决方法将是重新启动数据库服务器,这对于实时应用程序不是一件好事,因此请尽可能使用相同的连接。

要巩固所有这些概念,请参见包装psycopg2的以下示例

import psycopg2


class Postgres(object):
"""docstring for Postgres"""
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            # normally the db_credenials would be fetched from a config file or the enviroment
            # meaning shouldn't be hardcoded as follow
            db_config = {'dbname': 'demo', 'host': 'localhost',
                     'password': 'postgres', 'port': 5432, 'user': 'postgres'}
            try:
                print('connecting to PostgreSQL database...')
                connection = Postgres._instance.connection = psycopg2.connect(**db_config)
                cursor = Postgres._instance.cursor = connection.cursor()
                cursor.execute('SELECT VERSION()')
                db_version = cursor.fetchone()

            except Exception as error:
                print('Error: connection not established {}'.format(error))
                Postgres._instance = None

            else:
                print('connection established\n{}'.format(db_version[0]))

        return cls._instance

    def __init__(self):
        self.connection = self._instance.connection
        self.cursor = self._instance.cursor

    def query(self, query):
        try:
            result = self.cursor.execute(query)
        except Exception as error:
            print('error execting query "{}", error: {}'.format(query, error))
            return None
        else:
            return result

    def __del__(self):
        self.connection.close()
        self.cursor.close()

1
嗨!谢谢您的回答。但是,当我尝试实现这种情况时,我就有了if Database._instance is None: NameError: name 'Database' is not defined。我不明白这Database是什么以及如何解决。
伊利亚·鲁辛

1
@IlyaRusin是我的错,实际上数据库只是一个父类,在其中我放置了用于不同RDBMS处理的通用方法,因为我不仅连接到了Postgres。但是,请为该错误感到抱歉,希望更正的版本对您有用,如果有任何相关问题,请不要犹豫,随意添加,修改代码。
ponach

如果我要反复调用Postgres.query(Postgres(), some_sql_query)一个while循环,它会在每次迭代中仍然打开和关闭连接,还是在整个while循环时间内保持打开状态,直到程序退出?

@Michael连接类实现为单例,因此只能实例化一次,但总的来说,我建议反对建议的调用方式,而是以变量形式进行初始化
ponach

1
@ponach谢谢,它正是我想要实现的。我对您的代码进行了一些修改,并尝试在您的query()函数中使用UPDATE语句,但是当我以“并行”方式运行应用程序时,代码似乎出现了问题。我做了一个关于它的另外一个问题:softwareengineering.stackexchange.com/questions/399582/...

2

为对象提供上下文管理器功能会很有趣。这意味着您可以编写如下代码:

class MyClass:
    def __init__(self):
       # connect to DB
    def __enter__(self):
       return self
    def __exit__(self):
       # close the connection

通过使用with语句调用类,这将为您提供一种方便的方法来自动关闭与数据库的连接:

with MyClass() as my_class:
   # do what you need
# at this point, the connection is safely closed.

-1

考虑了很长时间。今天,我找到了路。我不知道这是最好的方法。您将创建一个名称为conn.py的文件,并将其保存在/usr/local/lib/python3.5/site-packages/conn/文件夹中。我使用freebsd,这是我的site-packages文件夹的路径。在我的conn.py中:conn =“ dbname = omnivore user = postgres password = 12345678”

``````````````````````````````````````````````````在脚本中我想调用connection,我这样写:

导入psycopg2导入psycopg2.extras导入psycopg2.extensions

从conn导入conn尝试:conn = psycopg2.connect(conn.conn)除外:page =“无法访问数据库”

cur = conn.cursor()

等等等等...

我希望这有用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.