如何创建跨模块变量



__debug__变量很方便,部分原因是它会影响每个模块。如果我想创建另一个以相同方式工作的变量,我将如何做?

变量(让我们保持原创并称之为"foo")不必是真正的全局变量,从某种意义上说,如果我在一个模块中更改foo,它会在其他模块中更新。如果我可以在导入其他模块之前设置 foo,然后他们会看到相同的值,那就太好了。

如果你需要一个全局跨模块变量,也许只是简单的全局模块级变量就足够了。

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

测试:

$ python b.py
# -> 1 2

真实世界的例子:Django 的global_settings.py(尽管在 Django 应用程序中,设置是通过导入对象django.conf.settings来使用的)。

我不以任何方式、形状或形式认可这个解决方案。 但是,如果您将变量添加到__builtin__模块中,则可以像从包含__builtin__的任何其他模块访问全局变量一样访问该变量 - 默认情况下,这是所有模块。

a.py 包含

print foo

b.py 包含

import __builtin__
__builtin__.foo = 1
import a

结果是打印"1"。

编辑:__builtin__模块可用作本地符号__builtins__ - 这就是这两个答案之间存在差异的原因。另请注意,__builtin__已在python3中重命名为builtins

我相信在

很多情况下它确实有意义,并且它简化了编程,使一些全局变量在几个(紧密耦合的)模块中已知。 本着这种精神,我想详细阐述一下拥有一个全局变量模块的想法,该模块由那些需要引用它们的模块导入。

当只有一个这样的模块时,我将其命名为"g"。 在其中,我为打算视为全局的每个变量分配默认值。 在使用其中任何一个的每个模块中,我不使用"from g import var",因为这只会导致一个局部变量,该变量仅在导入时从 g 初始化。 我以 g.var 的形式进行大多数引用,而"g."不断提醒我正在处理一个其他模块可能访问的变量。

如果这样一个全局变量的值在模块的某个函数中频繁使用,那么该函数可以创建一个本地副本:var = g.var。 但是,重要的是要认识到对 var 的赋值是本地的,如果不在赋值中显式引用 g.var,就无法更新全局 g.var。

请注意,您还可以让多个这样的全局模块由模块的不同子集共享,以使事情得到更严格的控制。 我为全局模块使用短名称的原因是为了避免它们出现过多地使代码混乱。 只需一点经验,它们就会变得足够助记,只有 1 或 2 个字符。

当 g 中尚未定义 x 时,仍然可以对 g.x 进行赋值,然后不同的模块可以访问 g.x。 然而,即使口译员允许,这种方法也不是那么透明,我确实避免了它。 由于赋值的变量名称中的拼写错误,仍有可能在 g 中意外创建新变量。 有时,检查dir(g)对于发现此类意外可能出现的任何意外名称很有用。

定义一个模块(称之为"globalbaz"),并在其中定义变量。所有使用此"伪全局"的模块都应导入"globalbaz"模块,并使用"globalbaz.var_name"引用它

无论更改的位置如何,这都有效,您可以在导入之前或之后更改变量。导入的模块将使用最新值。(我在一个玩具例子中测试过这个)

为了澄清起见,globalbaz.py 看起来像这样:

var_name = "my_useful_string"

您可以将一个模块的全局变量传递给另一个模块:

在模块 A 中:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

在模块 B 中:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

全局变量通常是一个坏主意,但您可以通过分配给 __builtins__ 来做到这一点:

__builtins__.foo = 'something'
print foo

此外,模块本身是可以从任何模块访问的变量。 因此,如果您定义一个名为 my_globals.py 的模块:

# my_globals.py
foo = 'something'

然后,您也可以从任何地方使用它:

import my_globals
print my_globals.foo

使用模块而不是修改__builtins__通常是执行此类全局变量的更简洁的方法。

您已经可以使用模块级变量执行此操作。 无论从哪个模块导入模块,模块都是相同的。 因此,您可以将变量设置为任何有意义的模块中的模块级变量,并从其他模块访问它或分配给它。 最好调用函数来设置变量的值,或使其成为某个单例对象的属性。 这样,如果您最终需要在变量更改时运行一些代码,则可以在不破坏模块的外部接口的情况下执行此操作。

这通常不是一种很好的做事方式——很少使用全局变量——但我认为这是最干净的方法。

我想发布一个答案,

即在某些情况下找不到变量。

周期性导入可能会破坏模块行为。

例如:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

在这个例子中,它应该是显而易见的,但在大型代码库中,这可能真的很令人困惑。

我想知道是否有可能通过使用类命名空间而不是全局/模块命名空间来传递变量的值来避免使用全局变量(例如 http://wiki.c2.com/?GlobalVariablesAreBad)的一些缺点。下面的代码指示这两种方法本质上是相同的。如下所述,使用类命名空间略有优势。

以下代码片段还显示,可以在全局/模块命名空间和类命名空间中动态创建和删除属性或变量。

wall.py

# Note no definition of global variables
class router:
    """ Empty class """

我称这个模块为"墙",因为它用于反弹变量。它将充当一个空间来临时定义空类"router"的全局变量和类范围属性。

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

该模块导入 wall 并定义单个函数sourcefn该函数定义消息并通过两种不同的机制发出消息,一种通过全局,另一种通过路由器函数。请注意,变量 wall.msgwall.router.message 是首次在其各自的命名空间中定义。

dest.py

import wall
def destfn():
    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'
    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

此模块定义了一个函数destfn,该函数使用两种不同的机制来接收源发出的消息。它允许变量"msg"可能不存在的可能性。 destfn 还会在显示变量后将其删除。

main.py

import source, dest
source.sourcefn()
dest.destfn() # variables deleted after this call
dest.destfn()

此模块按顺序调用先前定义的函数。在第一次调用dest.destfn变量后,变量wall.msgwall.router.msg不再存在。

该程序的输出为:

全球:你好世界!
路由器:你好世界!
全局:无消息
路由器:无消息

上面的代码片段表明,模块/全局和类/类变量机制本质上是相同的。

如果要共享大量变量,则可以通过使用多个 wall 类型模块(例如 wall1、wall2 等)或通过在单个文件中定义多个路由器类型类来管理命名空间污染。后者稍微整洁一些,因此也许代表了使用类变量机制的边际优势。

听起来像是修改__builtin__命名空间。 要做到这一点:

import __builtin__
__builtin__.foo = 'some-value'

不要直接使用__builtins__(注意额外的"s") - 显然这可以是字典或模块。 感谢ΤΖΩΤΖΙΟΥ指出这一点,可以在这里找到更多。

现在foo可以在任何地方使用。

我不建议一般这样做,但这取决于程序员。

分配给它必须如上所述完成,只需设置foo = 'some-other-value'只会在当前命名空间中设置它。

我把它用于几个我觉得真的缺少的内置原始函数。 一个例子是查找函数,它具有与过滤器、映射、化简相同的用法语义。

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d
import __builtin__
__builtin__.find = builtin_find

一旦运行(例如,通过在入口点附近导入),所有模块都可以使用 find(),就像很明显,它是内置的一样。

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

注意:当然,你可以用过滤器和另一行来测试零长度,或者在一种奇怪的行中使用reduce来做到这一点,但我一直觉得这很奇怪。

我可以使用字典实现跨模块可修改(或可变)变量:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60
# in myapp.mod1
from myapp import Timeouts
def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...
# in myapp.test.test_mod1
from myapp import Timeouts
def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

启动 test_wait_app_up_fail 时,实际超时持续时间为 3 秒。

相关内容

  • 没有找到相关文章

最新更新