如何制作跨模块变量?

时间:2008-09-26 23:59:47

标签: python module global

__debug__变量很方便,因为它影响每个模块。如果我想创建另一个以相同方式工作的变量,我该怎么做?

变量(让我们是原始的并称之为'foo')并不一定是真正的全局变量,因为如果我在一个模块中更改foo,它会在其他模块中更新。如果我可以在导入其他模块之前设置foo然后他们会看到相同的值,我会没事的。

12 个答案:

答案 0 :(得分:149)

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

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's global_settings.py(虽然在导入对象 django.conf.settings时使用了Django应用程序设置。)

答案 1 :(得分:106)

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

a.py包含

print foo

b.py包含

import __builtin__
__builtin__.foo = 1
import a

结果是打印“1”。

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

答案 2 :(得分:22)

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

无论更改位置如何,您都可以在导入之前或之后更改变量。导入的模块将使用最新值。 (我在玩具示例中对此进行了测试)

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

var_name = "my_useful_string"

答案 3 :(得分:22)

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

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

如果要在模块中的某个函数中频繁使用此类全局变量的值,则该函数可以生成本地副本:var = g.var。但是,重要的是要认识到var的赋值是本地的,并且如果没有在赋值中明确引用g.var,则无法更新全局g.var。

请注意,您还可以使用模块的不同子集共享多个此类全局模块,以使事情更加严格控制。我为我的全局模块使用短名称的原因是为了避免因出现它们而过多地混淆代码。只有一点点经验,它们只有1或2个字符就足够了。

当g尚未在g中定义时,仍然可以对g.x进行分配,然后另一个模块可以访问g.x.然而,即使翻译允许,这种方法也不是那么透明,我确实避免了。由于作业的变量名称中存在拼写错误,因此仍有可能在g中意外创建新变量。有时,对dir(g)的检查有助于发现此类事故可能产生的任何惊喜名称。

答案 4 :(得分:9)

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

在模块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

答案 5 :(得分:7)

全局变量通常是一个坏主意,但您可以通过分配__builtins__来完成此操作:

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

然后你也可以在任何地方使用它:

import my_globals
print my_globals.foo

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

答案 6 :(得分:5)

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

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

答案 7 :(得分:3)

我想发布一个答案,即有一个案例无法找到变量。

循环导入可能会破坏模块行为。

例如:

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

在这个示例中它应该是显而易见的,但在大型代码库中,这可能会让人感到困惑。

答案 8 :(得分:2)

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

import __builtin__
__builtin__.foo = 'some-value'

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

现在foo随处可用。

我不建议这样做,但是使用它取决于程序员。

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

答案 9 :(得分:1)

我将它用于一些我认为确实缺少的内置原始函数。一个例子是一个find函数,它具有与filter,map,reduce相同的用法语义。

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.

注意:当然,你可以用过滤器和另一条线来测试零长度,或者用一种奇怪的线条减少,但我总觉得它很奇怪。 / p>

答案 10 :(得分:1)

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

# 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秒。

答案 11 :(得分:0)

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

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

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

我称这个模块为“墙壁”。因为它用于弹跳变量。它将作为临时定义空类&#39;路由器的全局变量和类范围属性的空间。

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,它使用两种不同的机制来接收源发出的消息。它允许变量&#39; msg&#39;可能不存在。 destfn也会在显示变量后删除它们。

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

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

该计划的输出是:

全球:Hello world!
路由器:Hello world!
全球:没有消息
路由器:没有消息

上面的代码片段显示模块/全局和类/类变量机制基本相同。

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