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

Python名称空间和导入:在导入的模块中设置全局变量的值会创建第二个不相关的全局变量?,python,python-3.x,Python,Python 3.x,(Python 3.6) 考虑一个包含两个模块foo和bar的包odd。我希望奇数的客户对包中的所有内容使用单一导入,而不必知道foo和bar中定义了什么。因此,我定义了一个\uuuu init\uuuu.py,如下所示: from .foo import * from .bar import * foo.py包含一个全局变量baz和一个设置baz值的函数set_baz(),如下所示: baz = 12 def set_baz(): global baz print('Ba

(Python 3.6)

考虑一个包含两个模块foobar的包odd。我希望奇数的客户对包中的所有内容使用单一导入,而不必知道foo和bar中定义了什么。因此,我定义了一个\uuuu init\uuuu.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(从其他地方)并查看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,另一个用于odd.foo,每个名称空间对baz的定义不同

我如何实现我想要的,单个全局变量,由odd asodd.baz的客户访问


(是的,我知道我不应该使用这样的全局变量。我已经用odd.baz将我实际上试图实现的合理目标提炼到这个愚蠢的例子中,它说明了根本的问题。)

Python有名称,而不是变量。
如果在
odd.\uuuu init\uuuuu
中导入
odd.foo.baz
,它将创建一个新名称
odd.\uuuuu init\uuuuuuu.baz
指向相同的
int
对象
12

如果将名称
odd.foo.baz
更改为指向另一个
int
13
,则这对名称
odd.\uuuu init\uuuuuuu.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
,可以使用:

例如:

>>> 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'>
>test.baz
12
>>>test.set_baz()
巴兹12岁
现在改为13
>>>test.baz
13
>>>test.baz+4
17
>>>测试。baz.\u类__

try nonlocal而不是globalIn short:“from.foo import*”不会将导入模块中的名称公开到导入模块中。而是“from.foo import*”在导入模块中创建新名称,新名称最初采用与导入模块中相应名称相同的值。现在我明白了。谢谢
>>> 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'>