Python名称空间和导入:在导入的模块中设置全局变量的值会创建第二个不相关的全局变量吗?

时间:2018-05-23 14:17:54

标签: python python-3.x

(Python 3.6)

考虑一个包奇数,包含两个模块 foo bar 。我希望奇数的客户对包中的所有内容使用单个导入,而不必知道在foo中定义了什么以及在bar中定义了什么。所以我用以下内容定义了 __ init __。py

from .foo import *
from .bar import *

foo.py 包含一个全局变量 baz 和一个设置baz值的函数 set_baz(),如下所示:

baz = 12

def set_baz():
    global baz 
    print('Baz was {}'.format(baz))
    baz = 13
    print('Now it is changed to {}'.format(baz))

当我导入奇数(来自其他地方)并查看 odd.baz 的值时,我得到了我所期望的,原始值为12。

In [2]: import odd

In [3]: odd.baz
Out[3]: 12

但是当我运行 set_baz()时,它不会像预期的那样更改 odd.baz 的值,而是更改另一个全局变量: odd.foo.baz 即可。

In [4]: odd.set_baz()
Baz was 12
Now it is changed to 13

In [5]: odd.baz
Out[5]: 12

In [6]: odd.foo.baz
Out[6]: 13

显然有两个不相关的命名空间,一个用于奇数,一个用于 odd.foo ,每个命名空间用不同的方式定义 baz

如何实现我想要的,一个全局变量,由奇数的客户访问为 odd.baz

(是的,我知道我不应该使用像这样的全局变量。我已经将我实际想要完成的合理目标提炼到这个愚蠢的例子中,其中 odd.baz 说明了潜在的问题。)

1 个答案:

答案 0 :(得分:1)

Python有名字,而不是变量 如果您在odd.foo.baz中导入odd.__init__,则会创建一个新名称odd.__init__.baz,指向同一个int对象12。 如果您将名称odd.foo.baz更改为指向另一个int13,则这对名称odd.__init__.baz无效。

这种怪癖是全局被认为是不良行为的原因之一(非常特殊情况除外)。

您可以通过将baz封装在可变对象中来解决问题,例如dict

odd.foo.py

GLOBALS = {'baz': 12}

def set_baz():
    print('Baz was {}'.format(GLOBALS['baz']))
    GLOBALS['baz'] = 13
    print('Now it is changed to {}'.format(GLOBALS['baz']))

def get_baz():
    return GLOBALS['baz']

要使名称baz的行为与int相似,您可以使用werkzeug.local.LocalProxy

from werkzeug.local import LocalProxy

baz = LocalProxy(lambda: GLOBALS['baz'])

示例:

>>> test.baz
12
>>> test.set_baz()
Baz was 12
Now it is changed to 13
>>> test.baz
13
>>> test.baz + 4
17
>>> test.baz.__class__
<class 'werkzeug.local.LocalProxy'>