如何制作跨模块变量?


121

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

变量(让我们成为原始变量,并将其称为“ foo”)不必是真正的全局变量,在某种意义上,如果我在一个模块中更改foo,则在其他模块中对其进行更新。如果我可以在导入其他模块之前设置foo,然后它们会看到相同的值,那很好。

Answers:


114

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

a.py包含

print foo

b.py包含

import __builtin__
__builtin__.foo = 1
import a

结果是打印了“ 1”。

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


2
任何理由,您不喜欢这种情况?
Software Enthusiastic 2010年

31
一方面,它打破了人们在阅读代码时的期望。“这里使用的'foo'符号是什么?为什么看不到它的定义位置?”
Curt Hagenlocher 2010年

9
如果将来的Python版本开始使用您选择的实际内置名称,它也容易遭受破坏。
直觉

4
对于与导入的模块共享数据库连接之类的事情,这是一个很好的解决方案。作为健全性检查,我确保导入的模块assert hasattr(__builtin__, "foo")
Mike Ellis

4
对于任何阅读此答案的人:不要!做!这个 ! 真的不是
bruno desthuilliers

160

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

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)。


3
更好,因为它避免了可能的命名空间冲突
bgw 2010年

如果在这种情况下要导入的模块a.py包含该main()怎么办?有关系吗?
sedeh 2014年

4
@sedeh:不。如果a.py也作为脚本运行,则if __name__=="__main__"在其中使用防护,以避免在导入时运行意外的代码。
jfs 2014年

6
在现实世界中,您必须谨慎使用此解决方案。如果程序员使用“从import var”中获取“全局”变量,(请尝试c.py中的此变体),他们将在导入时获得该变量的副本。
保罗·惠普

1
@PaulWhipp:错误(提示:用于id()检查身份)
jfs

24

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

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

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

var_name = "my_useful_string"

24

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

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

如果此类全局变量的值要在模块中的某些函数中频繁使用,则该函数可以创建本地副本:var = g.var。但是,重要的是要认识到对var的分配是本地的,并且全局g.var不能在不显式引用分配中的g.var的情况下进行更新。

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

当x尚未在g中定义时,仍然可以对gx进行赋值,然后另一个模块可以访问gx。但是,即使解释器允许,这种方法也不是那么透明,我会避免它。由于赋值的变量名称中有错字,仍有可能意外地在g中创建新变量。有时,对dir(g)的检查对于发现此类事故可能引起的任何意外名称很有用。


7
这个有趣的发现解决了我的问题:“我不使用“ from g import var”,因为这只会导致局部变量,该局部变量仅在导入时才从g初始化。假定“ from..import”与“ import”相同似乎是合理的,但这不是事实。
Curtis Yallop 2014年

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

7

全局变量通常不是一个好主意,但是您可以通过分配给__builtins__

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

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

import my_globals
print my_globals.foo

__builtins__通常,使用模块而不是修改模块是执行此类全局操作的更干净的方法。


3
__builtins__是CPython的特性,您真的不应该使用它-更好地使用__builtin__(或builtins在Python3中使用),如公认的答案所示
Tobias Kienzler 2015年

5

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

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


3

我想发布一个答案,在某些情况下找不到该变量。

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

例如:

第一.py

import second
var = 1

第二个

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

main.py

import first

在这个例子中,它应该很明显,但是在大型代码库中,这确实很令人困惑。


1

这听起来像修改__builtin__名称空间。去做吧:

import __builtin__
__builtin__.foo = 'some-value'

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

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

我不建议一般这样做,但是使用此方法取决于程序员。

分配它必须按照上面的步骤进行,只是设置foo = 'some-other-value'只会在当前名称空间中进行设置。


1
我记得(来自comp.lang.python)应该避免直接使用内建函数。而是导入内置并使用它,如Curt Hagenlocher建议的那样。
tzot

1

我将此用于几个内置的原始函数,我觉得它们确实缺少了。一个示例是具有与filter,map,reduce相同的用法语义的find函数。

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.

注意:当然,您可以使用过滤器和另一条线来测试零长度,或者使用减少一种奇怪的线来执行此操作,但是我始终觉得这很奇怪。


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


1

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

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

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

我称此模块为“墙”,因为它是用来反弹变量的。它将用作临时定义空类“路由器”的全局变量和类范围属性的空间。

source.py

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

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

目的地

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等)或通过在单个文件中定义多个路由器类型的类来管理名称空间污染。后者稍微更整洁,因此也许代表了使用类变量机制的边际优势。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.