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

时间:2022-06-19 16:57:07

Is there a way to set up a global variable inside of a module? When I tried to do it the most obvious way as appears below, the Python interpreter said the variable __DBNAME__ did not exist.

是否有方法在模块内部设置全局变量?当我试图以最明显的方式进行时,Python解释器说变量__DBNAME__不存在。

...
__DBNAME__ = None

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

And after importing the module in a different file

在导入模块到另一个文件之后

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

And the traceback was: UnboundLocalError: local variable '__DBNAME__' referenced before assignment

回溯是:UnboundLocalError:赋值前引用的本地变量“__DBNAME__”

Any ideas? I'm trying to set up a singleton by using a module, as per this fellow's recommendation.

什么好主意吗?我试图通过使用一个模块来建立一个单例,这是这个人的建议。

5 个解决方案

#1


179  

Here is what is going on.

这就是发生的事情。

First, the only global variables Python really has are module-scoped variables. You cannot make a variable that is truly global; all you can do is make a variable in a particular scope. (If you make a variable inside the Python interpreter, and then import other modules, your variable is in the outermost scope and thus global within your Python session.)

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

All you have to do to make a module-global variable is just assign to a name.

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

Imagine a file called foo.py, containing this single line:

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

X = 1

Now imagine you import it.

现在假设你导入它。

import foo
print(foo.X)  # prints 1

However, let's suppose you want to use one of your module-scope variables as a global inside a function, as in your example. Python's default is to assume that function variables are local. You simply add a global declaration in your function, before you try to use the global.

但是,让我们假设您希望在函数中使用一个模块范围变量作为全局变量,如您的示例中所示。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.

顺便说一下,对于这个示例,简单的(如果不是__DBNAME__)测试就足够了,因为除了空字符串之外的任何字符串值都将求值true,因此任何实际的数据库名都将求值true。但是对于可能包含数值为0的变量,你不能只说如果不是变量名;在这种情况下,应该使用is操作符显式地测试None。我修改了示例以添加显式的无测试。对None的显式测试永远不会出错,所以我默认使用它。

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发出信号,您希望该变量对模块是“私有的”。如果您曾经做过从mymodule的导入*,Python将不会将带有两个前导下划线的名称导入到您的名称空间中。但是如果您只做一个简单的myimport模块,然后输入dir(mymodule),您将看到列表中的“private”变量,如果您显式地引用mymodule的话。__DBNAME__ Python不会介意,它只允许您引用它。双引导下划线是一个主要的提示,提示您的模块的用户,您不希望他们将这个名称重新绑定到他们自己的某个值。

It is considered best practice in Python not to do import *, but to minimize the coupling and maximize explicitness by either using mymodule.something or by explicitly doing an import like from mymodule import something.

在Python中,人们认为最好的做法不是做导入*,而是通过使用mymodule来最小化耦合并最大化显式。或者通过显式地从mymodule导入一些东西。

EDIT: If, for some reason, you need to do something like this in a very old version of Python that doesn't have the global keyword, there is an easy workaround. Instead of setting a module global variable directly, use a mutable type at the module global level, and store your values inside it.

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

In your functions, the global variable name will be read-only; you won't be able to rebind the actual global variable name. (If you assign to that variable name inside your function it will only affect the local variable name inside the function.) But you can use that local variable name to access the actual global object, and store data inside it.

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

You can use a list but your code will be ugly:

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

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

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

A dict is better. But the most convenient is a class instance, and you can just use a trivial class:

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

(You don't really need to capitalize the database name variable.)

(实际上不需要大写数据库名称变量)

I like the syntactic sugar of just using __m.dbname rather than __m["DBNAME"]; it seems the most convenient solution in my opinion. But the dict solution works fine also.

我喜欢用__m的句法糖。dbname而不是__m[“dbname”);在我看来,这似乎是最方便的解决办法。但是字典的解决方案也很好用。

With a dict you can use any hashable value as a key, but when you are happy with names that are valid identifiers, you can use a trivial class like Box in the above.

使用dict类型,您可以使用任何hashable值作为键,但是当您对有效标识符的名称感到满意时,可以使用上面的Box这样的普通类。

#2


40  

Explicit access to module level variables by accessing them explicity on the module

通过访问模块的明确性来显式访问模块级变量。


In short: The technique described here is the same as in steveha's answer, except, that no artificial helper object is created to explicitly scope variables. Instead the module object itself is given a variable pointer, and therefore provides explicit scoping upon access from everywhere. (like assignments in local function scope).

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

Think of it like self for the current module instead of the current instance !

把它看作是当前模块的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))

As modules are cached and therefore import only once, you can import db.py as often on as many clients as you want, manipulating the same, universal state:

由于模块被缓存,因此只能导入一次,所以可以导入db。尽可能多地使用您想要的客户端,操作相同的通用状态:

# client_a.py
import db

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

if (db_name == 'mongo'):
    db_name = None

I have used this recipe to create simple and lightweight handler modules (as opposed to handler classes) ever since I found it. As an additional bonus I find it quite pythonic overall as it nicely fits Pythons policy of Explicit is better than implicit.

自从我发现它以来,我使用这个菜谱创建了简单和轻量级的处理程序模块(与处理程序类相反)。作为一个额外的好处,我发现它很符合python的外显策略比内隐策略更好。

#3


17  

Steveha's answer was helpful to me, but omits an important point (one that I think wisty was getting at). The global keyword is not necessary if you only access but do not assign the variable in the function.

史蒂夫哈的回答对我很有帮助,但忽略了一个重要的观点(我想维斯蒂斯是这么说的)。如果只访问但不指定函数中的变量,则不需要全局关键字。

If you assign the variable without the global keyword then Python creates a new local var -- the module variable's value will now be hidden inside the function. Use the global keyword to assign the module var inside a function.

如果您在没有全局关键字的情况下分配变量,那么Python将创建一个新的局部var——模块变量的值现在将隐藏在函数中。使用global关键字在函数中分配模块var。

Pylint 1.3.1 under Python 2.7 enforces NOT using global if you don't assign the var.

在Python 2.7下的Pylint 1.3.1强制不使用全局变量。

module_var = '/dev/hello'

def readonly_access():
    connect(module_var)

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

#4


5  

For this, you need to declare the variable as global. However, a global variable is also accessible from outside the module by using module_name.var_name. Add this as the first line of your module:

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

global __DBNAME__

#5


-5  

You are falling for a subtle quirk. You cannot re-assign module-level variables inside a python function. I think this is there to stop people re-assigning stuff inside a function by accident.

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

You can access the module namespace, you just shouldn't try to re-assign. If your function assigns something, it automatically becomes a function variable - and python won't look in the module namespace.

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

You can do:

你能做什么:

__DB_NAME__ = None

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

but you cannot re-assign __DB_NAME__ inside a function.

但是不能在函数中重新分配__DB_NAME__。

One workaround:

一个解决方案:

__DB_NAME__ = [None]

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

Note, I'm not re-assigning __DB_NAME__, I'm just modifying its contents.

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

#1


179  

Here is what is going on.

这就是发生的事情。

First, the only global variables Python really has are module-scoped variables. You cannot make a variable that is truly global; all you can do is make a variable in a particular scope. (If you make a variable inside the Python interpreter, and then import other modules, your variable is in the outermost scope and thus global within your Python session.)

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

All you have to do to make a module-global variable is just assign to a name.

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

Imagine a file called foo.py, containing this single line:

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

X = 1

Now imagine you import it.

现在假设你导入它。

import foo
print(foo.X)  # prints 1

However, let's suppose you want to use one of your module-scope variables as a global inside a function, as in your example. Python's default is to assume that function variables are local. You simply add a global declaration in your function, before you try to use the global.

但是,让我们假设您希望在函数中使用一个模块范围变量作为全局变量,如您的示例中所示。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.

顺便说一下,对于这个示例,简单的(如果不是__DBNAME__)测试就足够了,因为除了空字符串之外的任何字符串值都将求值true,因此任何实际的数据库名都将求值true。但是对于可能包含数值为0的变量,你不能只说如果不是变量名;在这种情况下,应该使用is操作符显式地测试None。我修改了示例以添加显式的无测试。对None的显式测试永远不会出错,所以我默认使用它。

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发出信号,您希望该变量对模块是“私有的”。如果您曾经做过从mymodule的导入*,Python将不会将带有两个前导下划线的名称导入到您的名称空间中。但是如果您只做一个简单的myimport模块,然后输入dir(mymodule),您将看到列表中的“private”变量,如果您显式地引用mymodule的话。__DBNAME__ Python不会介意,它只允许您引用它。双引导下划线是一个主要的提示,提示您的模块的用户,您不希望他们将这个名称重新绑定到他们自己的某个值。

It is considered best practice in Python not to do import *, but to minimize the coupling and maximize explicitness by either using mymodule.something or by explicitly doing an import like from mymodule import something.

在Python中,人们认为最好的做法不是做导入*,而是通过使用mymodule来最小化耦合并最大化显式。或者通过显式地从mymodule导入一些东西。

EDIT: If, for some reason, you need to do something like this in a very old version of Python that doesn't have the global keyword, there is an easy workaround. Instead of setting a module global variable directly, use a mutable type at the module global level, and store your values inside it.

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

In your functions, the global variable name will be read-only; you won't be able to rebind the actual global variable name. (If you assign to that variable name inside your function it will only affect the local variable name inside the function.) But you can use that local variable name to access the actual global object, and store data inside it.

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

You can use a list but your code will be ugly:

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

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

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

A dict is better. But the most convenient is a class instance, and you can just use a trivial class:

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

(You don't really need to capitalize the database name variable.)

(实际上不需要大写数据库名称变量)

I like the syntactic sugar of just using __m.dbname rather than __m["DBNAME"]; it seems the most convenient solution in my opinion. But the dict solution works fine also.

我喜欢用__m的句法糖。dbname而不是__m[“dbname”);在我看来,这似乎是最方便的解决办法。但是字典的解决方案也很好用。

With a dict you can use any hashable value as a key, but when you are happy with names that are valid identifiers, you can use a trivial class like Box in the above.

使用dict类型,您可以使用任何hashable值作为键,但是当您对有效标识符的名称感到满意时,可以使用上面的Box这样的普通类。

#2


40  

Explicit access to module level variables by accessing them explicity on the module

通过访问模块的明确性来显式访问模块级变量。


In short: The technique described here is the same as in steveha's answer, except, that no artificial helper object is created to explicitly scope variables. Instead the module object itself is given a variable pointer, and therefore provides explicit scoping upon access from everywhere. (like assignments in local function scope).

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

Think of it like self for the current module instead of the current instance !

把它看作是当前模块的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))

As modules are cached and therefore import only once, you can import db.py as often on as many clients as you want, manipulating the same, universal state:

由于模块被缓存,因此只能导入一次,所以可以导入db。尽可能多地使用您想要的客户端,操作相同的通用状态:

# client_a.py
import db

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

if (db_name == 'mongo'):
    db_name = None

I have used this recipe to create simple and lightweight handler modules (as opposed to handler classes) ever since I found it. As an additional bonus I find it quite pythonic overall as it nicely fits Pythons policy of Explicit is better than implicit.

自从我发现它以来,我使用这个菜谱创建了简单和轻量级的处理程序模块(与处理程序类相反)。作为一个额外的好处,我发现它很符合python的外显策略比内隐策略更好。

#3


17  

Steveha's answer was helpful to me, but omits an important point (one that I think wisty was getting at). The global keyword is not necessary if you only access but do not assign the variable in the function.

史蒂夫哈的回答对我很有帮助,但忽略了一个重要的观点(我想维斯蒂斯是这么说的)。如果只访问但不指定函数中的变量,则不需要全局关键字。

If you assign the variable without the global keyword then Python creates a new local var -- the module variable's value will now be hidden inside the function. Use the global keyword to assign the module var inside a function.

如果您在没有全局关键字的情况下分配变量,那么Python将创建一个新的局部var——模块变量的值现在将隐藏在函数中。使用global关键字在函数中分配模块var。

Pylint 1.3.1 under Python 2.7 enforces NOT using global if you don't assign the var.

在Python 2.7下的Pylint 1.3.1强制不使用全局变量。

module_var = '/dev/hello'

def readonly_access():
    connect(module_var)

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

#4


5  

For this, you need to declare the variable as global. However, a global variable is also accessible from outside the module by using module_name.var_name. Add this as the first line of your module:

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

global __DBNAME__

#5


-5  

You are falling for a subtle quirk. You cannot re-assign module-level variables inside a python function. I think this is there to stop people re-assigning stuff inside a function by accident.

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

You can access the module namespace, you just shouldn't try to re-assign. If your function assigns something, it automatically becomes a function variable - and python won't look in the module namespace.

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

You can do:

你能做什么:

__DB_NAME__ = None

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

but you cannot re-assign __DB_NAME__ inside a function.

但是不能在函数中重新分配__DB_NAME__。

One workaround:

一个解决方案:

__DB_NAME__ = [None]

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

Note, I'm not re-assigning __DB_NAME__, I'm just modifying its contents.

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