是否有一种方法可以在模块内部设置一个全局变量?当我试图以如下所示的最明显的方式来做时,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')
...

回溯是:

... 在赋值前引用本地变量“DBNAME” ...

什么好主意吗?我试图通过使用模块设置一个单例,根据这个家伙的建议。


当前回答

你被一个微妙的怪癖骗了。不能在python函数中重新分配模块级变量。我认为这是为了防止人们在函数中意外地重新分配东西。

您可以访问模块名称空间,只是不应该尝试重新分配。如果你的函数赋值了一些东西,它会自动变成一个函数变量——python不会在模块命名空间中查找。

你可以:

__DB_NAME__ = None

def func():
    if __DB_NAME__:
        connect(__DB_NAME__)
    else:
        connect(Default_value)

但是你不能在函数内部重新分配__DB_NAME__。

一个解决方案:

__DB_NAME__ = [None]

def func():
    if __DB_NAME__[0]:
        connect(__DB_NAME__[0])
    else:
        __DB_NAME__[0] = Default_value

注意,我没有重新分配__DB_NAME__,我只是修改了它的内容。

其他回答

Steveha的回答对我很有帮助,但忽略了一个重要的点(我认为wisty正在抓住这个点)。如果只访问而不分配函数中的变量,则不需要使用global关键字。

如果你给变量赋值时不带global关键字,那么Python会创建一个新的局部var——模块变量的值现在会隐藏在函数内部。使用global关键字在函数中为模块var赋值。

Pylint 1.3.1在Python 2.7下强制NOT使用global如果你没有分配var。

module_var = '/dev/hello'

def readonly_access():
    connect(module_var)

def readwrite_access():
    global module_var
    module_var = '/dev/hello2'
    connect(module_var)

为此,需要将变量声明为全局变量。但是,全局变量也可以通过使用module_name.var_name从模块外部访问。添加这个作为模块的第一行:

global __DBNAME__

事情是这样的。

首先,Python真正拥有的唯一全局变量是模块范围内的变量。你不能创造一个真正全局的变量;你所能做的就是在特定的范围内创建一个变量。(如果你在Python解释器中创建了一个变量,然后导入其他模块,你的变量在最外层作用域,因此在你的Python会话中是全局的。)

创建一个模块全局变量所要做的就是赋值给一个名称。

想象一个名为foo.py的文件,它包含这样一行:

X = 1

现在想象你导入它。

import foo
print(foo.X)  # prints 1

但是,让我们假设您想要在函数中使用一个模块作用域变量作为全局变量,就像在您的示例中一样。Python的默认值是假设函数变量是本地的。在尝试使用全局变量之前,只需在函数中添加一个全局变量声明。

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.")

By the way, for this example, the simple if not __DBNAME__ test is adequate, because any string value other than an empty string will evaluate true, so any actual database name will evaluate true. But for variables that might contain a number value that might be 0, you can't just say if not variablename; in that case, you should explicitly test for None using the is operator. I modified the example to add an explicit None test. The explicit test for None is never wrong, so I default to using it.

Finally, as others have noted on this page, two leading underscores signals to Python that you want the variable to be "private" to the module. If you ever do an import * from mymodule, Python will not import names with two leading underscores into your name space. But if you just do a simple import mymodule and then say dir(mymodule) you will see the "private" variables in the list, and if you explicitly refer to mymodule.__DBNAME__ Python won't care, it will just let you refer to it. The double leading underscores are a major clue to users of your module that you don't want them rebinding that name to some value of their own.

在Python中,不执行import *被认为是最佳实践,而是通过使用mymodule来最小化耦合并最大化显式性。或者通过显式的导入,比如from mymodule import Something。

编辑:如果出于某种原因,你需要在一个没有global关键字的非常旧的Python版本中做类似的事情,有一个简单的解决方案。与其直接设置模块全局变量,不如在模块全局级别使用可变类型,并将值存储在其中。

在你的函数中,全局变量名将是只读的;您将无法重新绑定实际的全局变量名。(如果你在函数中赋值给那个变量名,它只会影响函数中的局部变量名。)但是您可以使用该局部变量名来访问实际的全局对象,并在其中存储数据。

你可以使用列表,但你的代码会很难看:

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

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

字典比较好。但最方便的是类实例,你可以只使用一个平凡的类:

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之类的普通类。

通过显式地访问模块上的模块级变量来显式地访问这些变量


简而言之:这里描述的技术与steveha的回答相同,只是没有人为创建帮助对象来显式地创建作用域变量。相反,模块对象本身被赋予了一个变量指针,因此在从任何地方访问时都提供了显式的范围。(类似于局部函数作用域中的赋值)。

把它看作是当前模块的self,而不是当前实例!

# db.py
import sys

# this is a pointer to the module object instance itself.
this = sys.modules[__name__]

# we can explicitly make assignments on it 
this.db_name = None

def initialize_db(name):
    if (this.db_name is None):
        # also in local function scope. no scope specifier like global is needed
        this.db_name = name
        # also the name remains free for local use
        db_name = "Locally scoped db_name variable. Doesn't do anything here."
    else:
        msg = "Database is already initialized to {0}."
        raise RuntimeError(msg.format(this.db_name))

由于模块是缓存的,因此只导入一次,你可以在尽可能多的客户端上导入db.py,操作相同的通用状态:

# client_a.py
import db

db.initialize_db('mongo')
# client_b.py
import db

if (db.db_name == 'mongo'):
    db.db_name = None  # this is the preferred way of usage, as it updates the value for all clients, because they access the same reference from the same module object
# client_c.py
from db import db_name
# be careful when importing like this, as a new reference "db_name" will
# be created in the module namespace of client_c, which points to the value 
# that "db.db_name" has at import time of "client_c".

if (db_name == 'mongo'):  # checking is fine if "db.db_name" doesn't change
    db_name = None  # be careful, because this only assigns the reference client_c.db_name to a new value, but leaves db.db_name pointing to its current value.

作为额外的奖励,我发现它整体上相当python化,因为它很适合python的显式优于隐式的策略。

你被一个微妙的怪癖骗了。不能在python函数中重新分配模块级变量。我认为这是为了防止人们在函数中意外地重新分配东西。

您可以访问模块名称空间,只是不应该尝试重新分配。如果你的函数赋值了一些东西,它会自动变成一个函数变量——python不会在模块命名空间中查找。

你可以:

__DB_NAME__ = None

def func():
    if __DB_NAME__:
        connect(__DB_NAME__)
    else:
        connect(Default_value)

但是你不能在函数内部重新分配__DB_NAME__。

一个解决方案:

__DB_NAME__ = [None]

def func():
    if __DB_NAME__[0]:
        connect(__DB_NAME__[0])
    else:
        __DB_NAME__[0] = Default_value

注意,我没有重新分配__DB_NAME__,我只是修改了它的内容。