如何在Python中创建模块范围的变量?

问题:

有没有办法在模块中设置全局变量?当我尝试这样做最明显的方式如下所示,Python解释器说变量__DBNAME__不存在。

...
__DBNAME__ = None

def initDB(name):
    if not __DBNAME__:
        __DBNAME__ = name
    else:
        raise RuntimeError("Database name has already been set.")
...

并将模块导入到不同的文件中

...
import mymodule
mymodule.initDB('mydb.sqlite')
...

回溯是:UnboundLocalError: local variable '__DBNAME__' referenced before assignment
有任何想法吗?根据this fellow’s建议,我正在使用一个模块来设置单例。

回答:

这是怎么回事
首先,Python真正具有的唯一全局变量是模块范围的变量。你不能做一个真正全球化的变量;所有你可以做的是在一个特定的范围内做一个变量。 (如果您在Python解释器中进行变量,然后导入其他模块,那么您的变量在最外层的范围内,因此在Python会话中是全局的。)
所有你需要做的只是将一个模块全局变量赋给一个名称。
想象一下名为foo.py的文件,其中包含以下单行:

X = 1

现在想象你导入它。

import foo
print(foo.X)  # prints 1

但是,假设您想将一个模块范围变量作为函数中的全局函数使用,如您的示例所示。 Python的默认是假定函数变量是本地的。在您尝试使用全局之前,您只需在函数中添加一个global声明。

def initDB(name):
    global __DBNAME__  # add this line!
    if __DBNAME__ is None: # see notes below; explicit test for None
        __DBNAME__ = name
    else:
        raise RuntimeError("Database name has already been set.")

顺便说一句,对于这个例子,简单的if not __DBNAME__测试是足够的,因为除了一个空字符串之外的任何字符串值都将被赋值为true,所以任何实际的数据库名称都将为true。但是对于可能包含可能为0的数字值的变量,您不能只说if not variablename;在这种情况下,您应该使用is运算符显式地测试None。我修改了示例来添加一个显式的None测试。对None的显式测试从未出错,所以我默认使用它。
最后,正如其他人在本页上注意到的那样,两个领先的下划线表示您希望变量对模块“私有”的Python。如果你有一个import * from mymodule,Python将不会将名称与两个领先的下划线一起导入你的名字空间。但是,如果你只是简单的import mymodule,然后说dir(mymodule)就会在列表中看到“私有”变量,如果你明确地提到mymodule.__DBNAME__ Python不会在意,那就只能让你参考它。双重领先的下划线是您的模块用户的主要线索,您不希望他们将该名称重新绑定到自己的某个价值。
在Python中,最好不要做import *,而是通过使用mymodule.something或显式地执行像from mymodule import something之类的导入来最小化耦合并最大化显式性,
编辑:如果由于某种原因,您需要在没有global关键字的旧版本的Python中执行此操作,那么有一个简单的解决方法。而不是直接设置模块全局变量,而是在模块全局级别使用可变类型,并在其中存储值。
在您的函数中,全局变量名称将为只读;您将无法重新绑定实际的全局变量名称。 (如果在函数内部分配给该变量名,它将仅影响函数内部的本地变量名称)。但是,您可以使用该局部变量名来访问实际的全局对象,并将数据存储在其中。
您可以使用list,但您的代码将是丑陋的:

__DBNAME__ = [None] # use length-1 list as a mutable

# later, in code:  
if __DBNAME__[0] is None:
    __DBNAME__[0] = name

A dict更好。但最方便的是一个类实例,你可以使用一个简单的类:

class Box:
    pass

__m = Box()  # m will contain all module-level values
__m.dbname = None  # database name global in module

# later, in code:
if __m.dbname is None:
    __m.dbname = name

(你真的不需要大写数据库名称变量。)
我喜欢刚刚使用__m.dbname而不是__m["DBNAME"]的句法糖;在我看来,这似乎是最方便的解决方案。但dict解决方案也很好。
使用dict,您可以使用任何可哈希值作为关键字,但是当您对有效标识符的名称感到满意时,您可以在上述中使用类似Box的琐碎类。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: How to create module-wide variables in Python?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

36 + = 42