以下是正在发生的事情。
首先,Python真正拥有的全局变量只有模块作用域变量。你不能创建一个真正意义上的全局变量;你所能做的就是在特定作用域内创建一个变量。(如果你在Python解释器内创建了一个变量,然后导入其他模块,你的变量将位于最外层作用域,从而成为你的Python会话中的全局变量。)
要创建一个模块全局变量,你只需要简单地给一个变量名赋值即可。
想象一个名为foo.py的文件,其中仅包含这一行:
X = 1
现在想象一下你将其导入。
import foo
print(foo.X)
然而,假设你想在函数中使用一个模块级变量作为全局变量,就像你的例子一样。Python 的默认设置是将函数变量视为局部变量。你只需要在函数中添加一个 global
声明,在尝试使用全局变量之前声明即可。
def initDB(name):
global __DBNAME__
if __DBNAME__ is None:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
顺便提一句,对于这个例子来说,简单的
if not __DBNAME__
测试就足够了,因为任何非空字符串都将被作为真值处理,所以任何实际的数据库名称都会被视为真。但是对于可能包含可能为0的数字值的变量,则不能仅使用
if not variablename
;在这种情况下,应该使用
is
运算符显式测试
None
。我修改了示例以添加一个明确的
None
测试。明确测试
None
永远不会出错,因此我默认使用它。
最后,正如其他人在本页面上所指出的,两个前导下划线表示您希望变量在模块内“私有”。如果您要执行
import * from mymodule
操作,Python将不会将具有两个前导下划线的名称导入您的命名空间中。但是,如果只需执行简单的
import mymodule
,然后说
dir(mymodule)
,您将看到列表中的“私有”变量,并且如果您明确引用
mymodule.__DBNAME__
,Python不会介意,它只会让您引用它。双下划线是向您的模块用户发出的重要提示,告诉他们您不希望他们将该名称重新绑定到他们自己的某个值。
在Python中,最好的做法是不要使用
import *
,而是通过
mymodule.something
或显式执行诸如
from mymodule import something
之类的导入,以最小化耦合并最大化明确性。
编辑:如果出于某种原因,您需要在Python的旧版本中执行此类操作,该版本没有
global
关键字,则有一种简单的解决方法。不要直接设置模块全局变量,而是在模块全局级别使用可变类型,并在其中存储您的值。
在函数中,全局变量名称将为只读;您将无法重新绑定实际的全局变量名称。(如果您在函数内部分配给该变量名称,则仅会影响函数内部的局部变量名称。)但是,您可以使用该局部变量名称访问实际的全局对象并在其中存储数据。
您可以使用一个
list
,但代码会很丑陋:
__DBNAME__ = [None]
if __DBNAME__[0] is None:
__DBNAME__[0] = name
使用 dict
更好。但最方便的是一个类实例,你可以使用一个简单的类:
class Box:
pass
__m = Box()
__m.dbname = None
if __m.dbname is None:
__m.dbname = name
(您实际上不需要将数据库名称变量大写。)
我喜欢使用__m.dbname
而不是__m["DBNAME"]
的语法糖;在我看来,这似乎是最方便的解决方案。但是dict
的解决方案也可以正常工作。
使用dict
时,您可以使用任何可哈希值作为键,但是当您满意于名称是有效标识符时,您可以像上面那样使用一个简单的类Box
。
__init __()
函数的方法,可以从kwargs
中获取所有值并在类字典中设置它们。然后,您只需执行_m = Box(dbname ="whatever")
即可使其整洁。自 Python 3.3以来,现在有types.SimpleNameSpace
,它是Box
类的全功能实现;见:https://docs.python.org/3/library/types.html#additional-utility-classes-and-functions - steveha