这是发生了什么。
首先,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__ 测试就足够了,因为除了空字符串之外的任何字符串值都将评估为真,因此任何实际的数据库名称都将评估为真。但是对于可能包含可能为 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
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 之类的普通类。