<p>这是发生的事情。</p>
<p>首先,Python真正拥有的唯一全局变量是模块范围的变量。不能生成真正全局的变量;只能在特定范围内生成变量。(如果您在Python解释器中创建一个变量,然后导入其他模块,那么您的变量在Python会话的最外层范围中,因此是全局的。)</p>
<p>创建模块全局变量所需做的只是为名称赋值。</p>
<p>想象一下一个名为foo.py的文件,其中包含这一行:</p>
<pre><code>X = 1
</code></pre>
<p>现在想象一下你导入它。</p>
<pre><code>import foo
print(foo.X) # prints 1
</code></pre>
<p>但是,假设您希望将模块范围变量之一用作函数中的全局变量,如您的示例所示。Python的默认设置是假设函数变量是局部的。您只需在函数中添加一个<code>global</code>声明,然后再尝试使用全局。</p>
<pre><code>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.")
</code></pre>
<p>顺便说一下,对于本例,简单的<code>if not __DBNAME__</code>测试就足够了,因为除了空字符串之外的任何字符串值都将计算为true,因此任何实际的数据库名称都将计算为true。但是对于可能包含数字值0的变量,不能只说<code>if not variablename</code>;在这种情况下,应该使用<code>is</code>运算符显式测试<code>None</code>。我修改了示例以添加一个显式的<code>None</code>测试。对<code>None</code>的显式测试从来没有错,所以我默认使用它。</p>
<p>最后,正如其他人在本页中所指出的,前面的两个下划线向Python发出信号,表示您希望变量对模块是“私有的”。如果您做过<code>import * from mymodule</code>,Python将不会将带有两个前导下划线的名称导入到您的名称空间中。但是如果你只做一个简单的<code>import mymodule</code>,然后说<code>dir(mymodule)</code>,你会看到列表中的“private”变量,如果你显式地引用<code>mymodule.__DBNAME__</code>,Python不会在意,它只会让你引用它。双前导下划线是模块用户的一个重要线索,您不希望他们将该名称重新绑定到自己的某个值。</p>
<p>在Python中,最好不要做<code>import *</code>,而是通过使用<code>mymodule.something</code>或显式地执行类似<code>from mymodule import something</code>的导入来最小化耦合并最大化显式性。</p>
<p>编辑:如果出于某种原因,您需要在没有<code>global</code>关键字的非常旧的Python版本中执行类似的操作,那么有一个简单的解决方法。不要直接设置模块全局变量,而是在模块全局级别使用可变类型,并将值存储在其中。</p>
<p>在函数中,全局变量名将是只读的;您将无法重新绑定实际的全局变量名。(如果在函数内指定该变量名,则只会影响函数内的局部变量名。)但可以使用该局部变量名访问实际的全局对象,并在其中存储数据。</p>
<p>您可以使用<code>list</code>,但您的代码将很难看:</p>
<pre><code>__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
</code></pre>
<p>A<code>dict</code>更好。但最方便的是类实例,您只需使用一个简单的类:</p>
<pre><code>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
</code></pre>
<p>(实际上不需要将数据库名称变量大写。)</p>
<p>我喜欢使用<code>__m.dbname</code>而不是<code>__m["DBNAME"]</code>的语法糖;在我看来,这似乎是最方便的解决方案。但是<code>dict</code>溶液也可以工作。</p>
<p>使用<code>dict</code>可以使用任何可散列值作为键,但是如果您对作为有效标识符的名称感到满意,则可以使用上面的<code>Box</code>之类的小类。</p>