如何创建跨模块变量?
- 2025-01-15 08:45:00
- admin 原创
- 99
问题描述:
该__debug__
变量非常方便,因为它会影响每个模块。如果我想创建另一个以相同方式工作的变量,我该怎么做?
该变量(我们不妨将其称为“foo”)不必是真正的全局变量,也就是说,如果我在一个模块中更改 foo,则其他模块中的变量也会更新。如果我可以在导入其他模块之前设置 foo,然后它们将看到相同的值,那就没问题了。
解决方案 1:
如果您需要一个全局跨模块变量,也许只需简单的全局模块级变量就足够了。
a.py:
var = 1
py:
import a
print a.var
import c
print a.var
复制代码
import a
a.var = 2
测试:
$ python b.py
# -> 1 2
真实世界的例子:Django 的 global_settings.py(尽管在 Django 应用程序中设置是通过导入对象 django.conf.settings
来使用的)。
解决方案 2:
我绝不认可这种解决方案。但是如果你向__builtin__
模块添加一个变量,它将像全局变量一样可以从任何其他包含的模块访问__builtin__
——默认情况下,所有模块都可以访问它。
a.py 包含
print foo
b.py 包含
import __builtin__
__builtin__.foo = 1
import a
结果是打印了“1”。
编辑:模块__builtin__
可用作本地符号__builtins__
——这就是这两个答案之间存在差异的原因。另请注意,在 python3 中__builtin__
已重命名为builtins
。
解决方案 3:
我认为在很多情况下这样做是有意义的,而且可以让一些全局变量在多个(紧密耦合的)模块中为人所知,这样可以简化编程。本着这种精神,我想详细阐述一下这样一种想法:让一个全局变量模块被那些需要引用它们的模块导入。
当只有一个这样的模块时,我将其命名为“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) 有助于发现可能因此类意外而产生的任何意外名称。
解决方案 4:
定义一个模块(称之为“globalbaz”)并在其中定义变量。所有使用此“伪全局”的模块都应导入“globalbaz”模块,并使用“globalbaz.var_name”引用它
无论更改的位置在哪里,这都可以正常工作,您可以在导入之前或之后更改变量。导入的模块将使用最新值。(我在一个玩具示例中测试了这一点)
为了澄清起见,globalbaz.py 看起来就像这样:
var_name = "my_useful_string"
解决方案 5:
您可以将一个模块的全局变量传递给另一个模块:
在模块 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
解决方案 6:
全局变量通常不是一个好主意,但你可以通过分配给来实现__builtins__
:
__builtins__.foo = 'something'
print foo
此外,模块本身也是可以从任何模块访问的变量。因此,如果您定义一个名为的模块my_globals.py
:
# my_globals.py
foo = 'something'
然后你也可以在任何地方使用它:
import my_globals
print my_globals.foo
使用模块而不是修改__builtins__
通常是执行此类全局变量的更简洁的方法。
解决方案 7:
您已经可以使用模块级变量来实现这一点。无论从哪个模块导入,模块都是相同的。因此,您可以将变量设置为模块级变量,无论将其放入哪个模块,都可以从其他模块访问它或为其赋值。最好调用一个函数来设置变量的值,或者使其成为某个单例对象的属性。这样,如果您最终需要在变量更改时运行某些代码,则可以这样做而不会破坏模块的外部接口。
这通常不是一种很好的做事方式 — — 使用全局变量也很少 — — 但我认为这是最干净的方式。
解决方案 8:
我想发表一个答案,说存在找不到变量的情况。
循环导入可能会破坏模块行为。
例如:
首先
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
在这个例子中,这应该是显而易见的,但在大型代码库中,这可能确实令人困惑。
解决方案 9:
我想知道,通过使用类命名空间而不是全局/模块命名空间来传递变量值,是否可以避免使用全局变量的一些缺点(例如,请参阅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
在此处首次在其各自的命名空间中定义。
目标文件
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
不再存在。
该程序的输出是:
全局:你好,世界!
路由器:你好,世界!
全局:无消息
路由器:无消息
上述代码片段表明模块/全局和类/类变量机制本质上是相同的。
如果要共享大量变量,则可以通过使用多个 wall 类型的模块(例如 wall1、wall2 等)或在一个文件中定义多个 router 类型的类来管理命名空间污染。后者稍微整洁一些,因此可能代表了使用类变量机制的边际优势。
解决方案 10:
这听起来像是修改__builtin__
名称空间。具体做法如下:
import __builtin__
__builtin__.foo = 'some-value'
不要__builtins__
直接使用(注意多余的“s”)——显然这可以是字典或模块。感谢 ΤΖΩΤΖΙΟΥ 指出这一点,更多信息可在此处找到。
现在foo
可在任何地方使用。
我一般不建议这样做,但是否使用取决于程序员。
对其分配必须按照上述方式进行,仅设置foo = 'some-other-value'
只会将其设置在当前命名空间中。
解决方案 11:
我将其用于一些我认为确实缺失的内置原始函数。其中一个例子是 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.
注意:当然,您可以使用过滤器和另一行来测试零长度,或者使用一种奇怪的行来减少长度,但我总是觉得这很奇怪。
解决方案 12:
我可以使用字典实现跨模块可修改(或可变)变量:
# 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秒。