在多个Python模块中共享数据库连接的正确方法

14

我想让一个数据库对象在多个Python模块中可用。

作为一个相关的例子,我创建了一个名为globl.py的文件:

DOCS_ROOT="c:\docs" ## as an example
SOLR_BASE="http://localhost:8636/solr/"

任何需要它的其他模块都可以执行

from globl import DOCS_ROOT

除了这个例子之外,我想要做的是使用数据库连接对象来完成同样的事情,在许多模块之间共享它们

import MySQLdb
conn = MySQLdb.connect (host="localhost"...)
cursor = conn.cursor()

我在解释器上尝试了这个:

from globl import cursor

看起来能够正常工作,但我怀疑这会导致每次从中导入时都执行同一个模块。所以这种方式合适吗?

3个回答

49
即使导入不会多次运行代码,这绝对不是正确的方式。相反,您应该将获取连接或游标的过程隐藏在函数后面。然后,您可以使用SingletonObject Pool设计模式来实现此功能。因此,它应该像这样:

db.py:

_connection = None

def get_connection():
    global _connection
    if not _connection:
        _connection = MySQLdb.connect(host="localhost"...)
    return _connection

# List of stuff accessible to importers of this module. Just in case
__all__ = [ 'getConnection' ]

## Edit: actually you can still refer to db._connection
##         if you know that's the name of the variable.
## It's just left out from enumeration if you inspect the module

someothermodule.py:

import db
conn = db.get_connection() # This will always return the same object

顺便提一下,根据你正在做的事情,与其共享连接对象而不是每次需要时创建一个新的对象可能并不是一个好主意。

但这就是为什么你想编写一个get_connection()方法,在代码的其余部分中抽象出这些问题。


很棒的答案,这个也是。如果我要选择的话,我会选这个,因为它教了我新东西。 - Jesvin Jose
如果我运行这段代码,会出现UnboundLocalError: local variable '_connection' referenced before assignment。 - Jim
@Jim,没错。那只是一个匆忙的例子。我已经添加了一行代码 global _connection - Zecc
使用 __all__ 还有一个好处,即在使用 import * 导入名称空间时,可以表示将要导入的内容。不过如果你能避免使用 import * 的话,最好不要使用它! :-) - deed02392

5
你的猜测是错误的。 代码只会被执行一次 - 后续导入只通过 sys.modules 引用该模块,不会重新运行它。
(请注意,只要始终使用相同的路径导入模块,这就是这种情况的情况 - 如果在一个地方使用 from globl import cursor,并且在另一个地方使用 from my.fullyqualified.project.global import cursor,则可能会发现代码正在重新执行。) 编辑添加,如S.Lott在评论中所说,这是处理全局对象的完全有效的方法。

1
这种方式是否妥当?我认为你应该指出,像这样的模块全局变量在长期运行中非常有效。 - S.Lott

1

我认为Daniel已经回答了这个问题,但我想补充一下关于你想分享的游标对象的几点评论。

通常不建议以这种方式共享游标对象。当然,这取决于你的程序是什么,但作为一般解决方案,我建议你将此游标对象隐藏在生成游标的“工厂”后面。基本上,你可以创建一个方法cursor()get_cursor(),而不是将游标作为全局变量。主要好处(但不是唯一的好处)- 你可以在这个“工厂”后面隐藏更复杂的逻辑 - 池化、自动重新连接(如果连接断开)、等等。即使你现在不需要它,如果你现在开始使用这种方法,稍后添加它也会非常容易,而且现在你可以将此函数实现保持简单,如return _cursor

是的,模块本身只会被导入一次。


我将来会参考这个。这是一种通过简单的界面实现复杂功能的好方法。 - Jesvin Jose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接