该__debug__
变量很方便,部分原因是它会影响每个模块。如果我想创建另一个以相同方式工作的变量,我该怎么做?
变量(让我们保持原始状态并称其为“foo”)不必是真正的全局变量,因为如果我在一个模块中更改 foo,它会在其他模块中更新。如果我可以在导入其他模块之前设置 foo 然后他们会看到相同的值,我会很好。
如果您需要一个全局跨模块变量,那么简单的全局模块级变量可能就足够了。
一个.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__
——这就是这两个答案之间存在差异的原因。另请注意,在 python3__builtin__
中已重命名为。builtins
定义一个模块(称之为“globalbaz”)并在其中定义变量。所有使用这个“pseudoglobal”的模块都应该导入“globalbaz”模块,并使用“globalbaz.var_name”引用它
无论更改的位置如何,这都有效,您可以在导入之前或之后更改变量。导入的模块将使用最新的值。(我在一个玩具示例中对此进行了测试)
为了澄清,globalbaz.py 看起来像这样:
var_name = "my_useful_string"
我相信在很多情况下它确实有意义,并且它简化了编程以拥有一些跨多个(紧密耦合)模块已知的全局变量。本着这种精神,我想详细说明一下拥有一个由需要引用它们的模块导入的全局模块的想法。
当只有一个这样的模块时,我将其命名为“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) 的检查有助于发现此类意外可能出现的任何意外名称。
您可以将一个模块的全局变量传递给另一个模块:
在模块 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__
通常是执行此类全局变量的更干净的方法。
您已经可以使用模块级变量执行此操作。无论从哪个模块导入模块,它们都是相同的。因此,您可以在任何有意义的模块中将变量设置为模块级变量,并从其他模块访问或分配给它。最好调用一个函数来设置变量的值,或者使其成为某个单例对象的属性。这样,如果您最终需要在变量更改时运行一些代码,您可以这样做而不会破坏模块的外部接口。
这通常不是做事的好方法——很少使用全局变量——但我认为这是最干净的方法。
我想发布一个答案,即存在找不到变量的情况。
循环导入可能会破坏模块行为。
例如:
第一个.py
import second
var = 1
第二个.py
import first
print(first.var) # will throw an error because the order of execution happens before var gets declared.
主文件
import first
在这个例子中应该很明显,但在大型代码库中,这可能真的很混乱。
我想知道是否可以通过使用类命名空间而不是全局/模块命名空间来避免使用全局变量的一些缺点(参见例如http://wiki.c2.com/?GlobalVariablesAreBad)来传递变量的值. 下面的代码表明这两种方法本质上是相同的。如下所述,使用类命名空间有一点优势。
以下代码片段还显示了属性或变量可以在全局/模块命名空间和类命名空间中动态创建和删除。
墙.py
# Note no definition of global variables
class router:
""" Empty class """
我将此模块称为“墙”,因为它用于反弹变量。它将充当临时定义空类“路由器”的全局变量和类范围属性的空间。
源码.py
import wall
def sourcefn():
msg = 'Hello world!'
wall.msg = msg
wall.router.msg = msg
此模块导入 wall 并定义一个函数sourcefn
,该函数定义消息并通过两种不同的机制发出消息,一种通过全局变量,另一种通过路由器函数。请注意,变量wall.msg
和wall.router.message
是第一次在其各自的命名空间中定义。
目标.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
变量显示后也会删除。
主文件
import source, dest
source.sourcefn()
dest.destfn() # variables deleted after this call
dest.destfn()
该模块依次调用前面定义的函数。第一次调用后的dest.destfn
变量又不存在了。wall.msg
wall.router.msg
该程序的输出是:
全球:世界你好!
路由器:世界你好!
全局:无消息
路由器:无消息
上面的代码片段表明模块/全局和类/类变量机制本质上是相同的。
如果要共享大量变量,可以通过使用几个墙类型模块(例如 wall1、wall2 等)或通过在单个文件中定义多个路由器类型类来管理名称空间污染。后者稍微整洁一些,因此可能代表使用类变量机制的边际优势。
这听起来像是在修改__builtin__
名称空间。去做吧:
import __builtin__
__builtin__.foo = 'some-value'
不要__builtins__
直接使用(注意额外的“s”)——显然这可以是字典或模块。感谢 ΤZΩΤZΙΟΥ 指出这一点,更多可以在这里找到。
现在foo
可以在任何地方使用。
我一般不建议这样做,但使用它取决于程序员。
分配给它必须像上面那样完成,只是设置foo = 'some-other-value'
只会将它设置在当前命名空间中。
我将它用于我觉得确实缺少的几个内置原始函数。一个例子是 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.
注意:当然,您可以使用 filter 和另一行来测试零长度,或者在一种奇怪的行中使用 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 秒。