如何制作跨模块变量?

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

这个变量(我们称之为“ foo”)不一定是真正的全局变量,也就是说,如果我在一个模块中更改了 foo,那么在其他模块中就会更新它。如果我可以在导入其他模块之前设置 foo,那么他们将看到相同的值,那么我就没问题了。

182401 次浏览

这听起来像是修改 __builtin__名称空间:

import __builtin__
__builtin__.foo = 'some-value'

不要直接使用 __builtins__(注意多余的“ s”)-显然这可以是一个字典或模块。多亏了 Ω,你指出了这一点,我们可以找到更多的 给你

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

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

赋值给它必须像上面那样,只需设置 foo = 'some-other-value'就可以在当前名称空间中设置它。

我不赞成这种解决方案的任何方式,形式或形式。但是,如果向 __builtin__模块添加一个变量,那么它将可以访问,就好像来自包含 __builtin__的任何其他模块的全局变量一样——默认情况下,__builtin__是所有这些模块的全局变量。

A.py 包含

print foo

B.py 包含

import __builtin__
__builtin__.foo = 1
import a

结果是“1”被打印出来。

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

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

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

需要澄清的是,globalaz.py 看起来就像这样:

var_name = "my_useful_string"

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

阿比:

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 _ sets. py(尽管在 Django 应用程序中通过导入 对象 django.conf.settings来使用设置)。

全局变量通常是个坏主意,但是你可以通过赋值 __builtins__来实现:

__builtins__.foo = 'something'
print foo

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

# my_globals.py
foo = 'something'

那么你也可以在任何地方使用它:

import my_globals
print my_globals.foo

使用模块而不是修改 __builtins__通常是做这种类型的全局变量的一种更干净的方法。

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

单元甲:

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

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

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

我用它来实现一些内置的原始函数,我觉得这些函数实际上是缺失的。一个例子是 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 来测试某种奇怪的行,但是我总觉得它很奇怪。

我相信在很多情况下,它确实有意义,并且它简化了编程,使得一些全局变量可以跨多个(紧耦合的)模块使用。本着这种精神,我想稍微阐述一下有一个全局模块的想法,这个全局模块是由那些需要引用它们的模块导入的。

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

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

请注意,您还可以有多个这样的全局模块,由您的模块的不同子集共享,以使控制更加严格一些。我为全局变量模块使用短名称的原因是为了避免代码中出现过多的短名称。只需要一点点经验,他们就可以通过1到2个字符来完成记忆。

当 x 还没有在 g 中定义的时候,仍然可以对 g. x 进行赋值,然后一个不同的模块可以访问 g. x。然而,即使解释器允许这样做,这种方法也不是那么透明,我确实避免这样做。由于赋值的变量名中的输入错误,仍然有可能意外地在 g 中创建一个新变量。有时,对 dir (g)的检查有助于发现这种意外事件可能产生的任何令人吃惊的名称。

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

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

我想发表一个答案,有一个情况下,变量不会被发现。

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

例如:

首先是 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.

Main Py

import first

在这个例子中,它应该是显而易见的,但是在一个大的代码库中,这可能会非常混乱。

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

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

Wallpy

# 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.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不再存在。

该方案的输出是:

你好,世界!
路由器: 你好,世界!
全球: 没有消息
路由器: 没有消息

上面的代码片段显示模块/global 和 class/class 变量机制在本质上是相同的。

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