Python: 在模块和类之间共享全局变量

37
我知道在Python中可以跨模块共享全局变量。但我想知道这种做法的范围以及原因。例如,
global_mod.py
x = None

mid_access_mod.py

from global_mod import *

class delta:
    def __init__(self):
        print x

bot_modif_mod.py

import mid_access_mod
import global_mod

class mew:
    def __init__(self):
        global_mod.x = 5

def main():
    m = mew()
    d = mid_access_mod.delta()

尽管所有模块都共享全局变量x,但这将打印出None。为什么会这样呢?似乎在mid_access_mod.py中,在bot_modif_mod.py中的mew()函数分配之前就已经计算了x。

5个回答

39
这是因为您正在使用不可变值(整数和None),导入变量就像通过值传递而不是引用传递。
如果您将global_mod.x设置为列表并操作其第一个元素,则它将按预期工作。
当您执行from global_mod import x时,您正在创建一个名称x在您的模块中具有与global_mod中相同值的名称。对于函数和类之类的东西,这可以正常工作,因为人们通常不会稍后重新分配那些名称。
正如Alex指出的那样,如果您使用import global_mod,然后使用global_mod.x,您将避免该问题。您在模块中定义的名称将是global_mod,它始终指向您想要的模块,然后使用属性访问来获取x将获取x的最新值。

5
我认为你在这里的回答是误导性的。问题的主要原因不是可变类型或不可变类型的使用,而是使用了 from modulename import * 而不是 import modulename。如果你将 emish 的代码中的 global_mod.py 更改为 x = ['starting list'],然后更改 bot_modif_mod.py 中的重新分配为 global_mod.x = ['new list'],你会发现这将打印 "['starting list']"。正如 Alex Martinelli 所说,真正的问题是 from modulename import * 快照。我希望其他用户不会像我一样感到困惑。 - user1379351
我相信@Ned想要表达的是,假设x=['starting list'],那么应该通过x[0]='new value'来编辑值,而不是将值分配为全新的列表(x=['new list'])。 - KFox

27

from whatever import * 不是一个好的编程习惯,它只是用来在交互式会话中节省一些打字时间的快捷方式。它基本上“快照”了模块中所有名称在那个时间点的状态 -- 如果你重新绑定了其中任何一个名称,那么这个快照就会变得过时,从而导致各种问题。而且,使用可怕的 from ... import * 结构只是你签署的不可避免混乱的开始。

想听我的建议吗?最好忘记你曾经听说过这个结构,并永远不要再使用它。使用 import global_mod as m 并始终使用 限定的 名称,例如 m.x -- 在Python中,限定名称比仅仅使用裸名更加方便和强大,这一点甚至不需要解释。( as m 部分是完全可选的,基本上是为了简洁起见,或者有时为了解决名称冲突的问题; 当你觉得方便时可以使用它,因为它没有任何缺点,但是当你不觉得必要时不要被迫或者感到被敦促去使用它)。


0
正如Ned Batchelder所提到的,只有值是共享的,而不是实际对象。如果您想通过引用共享对象,则可能正在寻找“线程本地数据”。
例如:
import threading

g = threading.local()
g.population = '7 Billion'

现在,每当你想要访问或修改变量 g.population 时,只要它是来自同一线程,你就会得到其更新后的值。

了解更多信息,请参阅 Python 文档:https://docs.python.org/3/library/threading.html#thread-local-data


0

我修改了示例,使用列表来代替x,并按照顶部答案中建议的方式进行列表分配(x [0] = ..),并且打印返回了相同的初始值(None)...这证实了“from global_mod import *”是一个副本,无论可变与否。

如评论中所建议的,“import global_mod”可以正常工作,如果在“mid_access_mod”中使用“print global_mod.x =”。


0
为了解决这个问题,只需将 from global_mod import * 改为 import global_mod
新的 mid_access_mod.py 将会是:
import global_mod

class delta:
    def __init__(self):
        print global_mod.x

原因可以在这里找到。

由于Python中引用和名称绑定的方式,如果您想要从模块外部更新模块中的一些符号,比如foo.bar,并且让其他导入代码“看到”该更改,您必须以某种特定的方式导入foo。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接