Python 模块中的全局变量自发重置

Gre*_*g T 5 python python-3.x

我有一个用 Python 3.5 编写的程序的一部分,并从测试前两个模块开始。我设法隔离其中一个模块中的一个问题,其中两个全局变量似乎无缘无故地切换回其原始值。这些全局变量之一 ( event_count) 仅在单个函数中使用(grep 显示字符串“event_count”没有出现在我的任何 *.py 文件中的其他任何位置),但变量的值在函数调用之间发生变化。如果我为此模块中的其他全局变量添加打印语句,它也会同时恢复为原始值。移动event_count到另一个模块(用sensorlogic.event_countin替换它eventcount()并将初始化移动到另一个模块)会使该行为消失,所以我有一个修复但不理解。

event_count以下是模块中使用 , 的所有代码sensoreval

event_count = 0

def eventcount(increment):
    global event_count
    print("entering eventcount, increment =", increment,
          ", event_count =", event_count)
    event_count += increment
    print("leaving eventcount, event_count =", event_count)
    return event_count
Run Code Online (Sandbox Code Playgroud)

如果我运行以下代码段:

    e.setvalue(1)
    print("I am at marker #1")
    eventcount(0)
Run Code Online (Sandbox Code Playgroud)

( 中的最后一个操作e.setvalue()是调用eventcount(0))它会产生以下输出:

entering eventcount, increment = 0 , event_count = 4
leaving eventcount, event_count = 4
I am at marker #1
entering eventcount, increment = 0 , event_count = 0
leaving eventcount, event_count = 0
Run Code Online (Sandbox Code Playgroud)

我尝试将这两个模块缩小到合理的大小,但当我这样做时,问题就消失了。我会继续努力。由于我以前从未使用过 Python 3,并且只有一点 Python 2.7 经验,所以我认为我在做一些愚蠢的事情,我只是不知道是什么。

我相信我的示例与一些已指出的相关帖子不同,因为该变量event_count只是全局的,因此它将是静态的。它仅在这个单一函数中使用。字符串“event_count”不会出现在该模块或任何其他模块的任何其他位置。


经过多次编辑/重新运行迭代后,我有一个易于管理的小示例来演示正在发生的情况。它涉及两个模块,总共8行代码。第一个模块a.py__main__

import b
c = 0
if __name__ == '__main__': 
    b.init()
    print("c =", c)
Run Code Online (Sandbox Code Playgroud)

第二个模块是b.py

import a
def init():
    a.c = 1
Run Code Online (Sandbox Code Playgroud)

运行a.py产生输出:

c = 0
Run Code Online (Sandbox Code Playgroud)

我预计仍然c是 1 。a.c = 1b.py

另外,我尝试通过删除if __name__ == '__main__'from 来进一步减少这一点a.py,但随后该示例不再运行:

Traceback (most recent call last):
  File "...\a.py", line 1, in <module>
    import b
  File "...\b.py", line 1, in <module>
    import a
  File "...\a.py", line 3, in <module>
    b.init()
AttributeError: module 'b' has no attribute 'init'
Run Code Online (Sandbox Code Playgroud)

我也无法解释这一点,但它似乎可能是相关的。


按照 Mata 的指导,我相信下面的代码显示了发生了什么。涉及三个模块。a.py

print("__name__ =", __name__)
import b
print("__name__ =", __name__)
def f(): pass
print(f)
if __name__ == '__main__':
    print("f is b.a.f?", f is b.a.f)
Run Code Online (Sandbox Code Playgroud)

b.py

import a
Run Code Online (Sandbox Code Playgroud)

c.py

import a
import b
print("__name__ =", __name__)
print("a.f is b.a.f?", a.f is b.a.f)
Run Code Online (Sandbox Code Playgroud)

您可以通过运行来查看问题a.py,给出结果:

__name__ = __main__
__name__ = a
__name__ = a
<function f at 0x0000021A4A947840>
__name__ = __main__
<function f at 0x0000021A484E0400>
f is b.a.f? False
Run Code Online (Sandbox Code Playgroud)

运行c.py__main__不是导入周期的一部分会导致:

__name__ = a
__name__ = a
<function f at 0x000001EA101B7840>
__name__ = __main__
a.f is b.a.f? True
Run Code Online (Sandbox Code Playgroud)

Mad*_*ist 3

让我们逐步看一下您的两个模块示例。那里的行为是预期的,但最初令人困惑,并且可能很好地解释了其他情况下发生的情况。

a如果您作为脚本运行,它不会导入asys.modules,而是导入为__main__。第一条语句是import b,它创建一个空模块对象sys.modules['b']并开始初始化它。

b再次进口一线asys.modules['a']通常,会找到下面的模块对象,但在这种情况下,您a作为脚本运行,因此初始导入发生在不同的名称下。由于这次aisa和 not的名称被设置为零,并且没有其他事情发生。__main__a.c

现在执行返回到b. 它现在创建一个函数init,并将其设置sys.modules['a'].c为 1。我非常明确地写出了对该模块的引用,a因为这是造成差异的根本原因。

一旦b导入,执行将返回到a,但不会返回sys.modules['a']。下一行c = 0实际上设置sys.modules['__main__'].c为零。希望此时您能看到问题所在。下一行调用b.init,将其设置sys.modules['a']为 1。然后打印sys.modules['__main__'],正如预期的那样,它为零。

为了验证这个说明的正确性,尝试添加一条打印语句

print(sys.modules['a'].c)
Run Code Online (Sandbox Code Playgroud)

你会得到1。还有,sys.modules['a'] is sys.modules['__main__'].c将会False。解决这个问题的最简单方法是不在给定模块的导入中初始化其他模块的成员。

您的具体案例记录如下:http://effbot.org/zone/import-confusion.htm#using-modules-as-scripts

其他资源

您可以在此处获取有关导入系统的详细信息: https: //docs.python.org/3/reference/import.html。这里描述了导入的各种陷阱和警告:http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html