如何在 Python 中创建模块范围的变量?[重复]

2025-04-15 09:19:00
admin
原创
25
摘要:问题描述:有没有办法在模块内部设置全局变量?当我尝试用下面最明显的方式执行此操作时,Python 解释器说该变量__DBNAME__不存在。... __DBNAME__ = None def initDB(name): if not __DBNAME__: __DBNAME__ = ...

问题描述:

有没有办法在模块内部设置全局变量?当我尝试用下面最明显的方式执行此操作时,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')
...

回溯结果如下:

UnboundLocalError: local variable '__DBNAME__' referenced before assignment

有什么想法吗?我正在尝试按照这位朋友的建议,使用模块来设置单例。


解决方案 1:

事情是这样的。

首先,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__测试就足够了,因为除了空字符串之外的任何字符串值都会被评估为 true,所以任何实际的数据库名称都会被评估为 true。但是对于可能包含数字值(该数字值可能为 0)的变量,您不能直接使用if not variablename;在这种情况下,您应该None使用is运算符显式地测试 。我修改了示例以添加一个显式None测试。显式测试None永远不会出错,所以我默认使用它。

最后,正如其他人在本页所指出的,双下划线开头会向 Python 发出信号,表明您希望该变量对模块来说是“私有的”。如果您执行了from mymodule import *,Python 不会将以双下划线开头的名称导入到您的命名空间中。但是,如果您只是执行了一个简单的import mymodule,然后 say ,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

Adict更好。但最方便的是一个类实例,你可以直接使用一个简单的类:

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上面那样的简单类。

解决方案 2:

通过在模块上显式访问模块级变量来显式访问它们


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

可以将其视为当前模块自身,而不是当前实例!

# 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 的“显式优于隐式”策略。

解决方案 3:

Steveha 的回答对我有帮助,但忽略了一个重要点(我认为 Wisty 想表达的就是这个点)。如果在函数中只访问变量但不赋值,那么 global 关键字就没有必要了。

如果在赋值时不使用 global 关键字,Python 会创建一个新的局部变量——模块变量的值现在将隐藏在函数内部。使用 global 关键字可以在函数内部赋值模块变量。

如果您没有分配变量,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:

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

global __DBNAME__

解决方案 5:

你被一个微妙的怪癖迷惑了。你不能在 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__,我只是修改了它的内容。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用