我有两个python脚本,它们相互导入并访问第一个脚本中定义的变量。但是在其他模块中没有看到对该变量的更改。
可以复制行为的代码:
文件a.py
import time
import b
X = {}
def set():
global X
X[time.time()] = 1
def get():
global X
return X
if __name__ == '__main__':
b.monitor()
while True:
time.sleep(1)
set()
文件b.py
:
import threading
import time
from a import get
def f():
while True:
time.sleep(1)
print get(), 'in b'
def monitor():
t = threading.Thread(target=f)
t.setDaemon(True)
t.start()
使用python a.py
运行程序。结果是:
{} in b
{} in b
{} in b
...
预期输出:
{"1518106774": 1} in b
{"1518106774": 1, "1518106775": 1} in b
{"1518106774": 1, "1518106775": 1, "1518106776": 1} in b
... // etc
...键是time.time()
字符串。
我认为,无论我们想要的何处,全局变量X
都应被引用。那么,为什么X
在b.f
中未更新,即使对set
的调用将这些键添加到它?
问题是,在这样的设置中创建了变量X的两个实例:一个在a.py
中,另一个在b.py
中创建。这个问题也发生在此示例中,将其简化为本质 - 它与创建线程无关:
最简单形式的问题
文件a.py
:
X = "original value"
import b
if __name__ == '__main__':
b.monitor()
print ('A', X)
文件b.py
:
import a
def monitor():
print ('B', a.X)
a.X = "new value"
print ('B', a.X)
运行a.py
时输出为:
b原始价值
B新值
原始值
如果访问了相同的X
,则输出的最后一行将读取:
新值
解释
这是因为import
创建了其中所有全局名称的新实例。它将仅在第一次遇到import a
时才执行此操作,这确实是这里第一次。a.py
的执行创建的模块命名为__main__
,而不是a
。由于此名称与a
不同,因此import a
确实会导入模块,而不仅仅是引用已经加载的__main__
模块。另请参见Python文档,以获取有关模块名称的解析的说明。
因此,这两个模块中的名称是不同的。import a
不能使其他模块可以访问__main__
名称。
解决方案
快速解决方案是用import __main__
中的import a
语句替换CC_27语句,并使用__main__.X
作为您要在b.py
中访问的合格名称。但这不是被认为继续进行的建议。
一种更健壮的方法是确保在执行脚本中未定义全局变量,而是在导入的模块中 - 由 a.py
和 b.py
导入的模块。第二个import
将使第一个import
解析时定义的名称:
文件common.py
:
X = "original value"
文件a.py
:
import common
import b
if __name__ == '__main__':
b.monitor()
print ('A', common.X)
文件b.py
:
import common
def monitor():
print ('B', common.X)
common.X = "new value"
print ('B', common.X)
现在输出为:
b原始价值
B新值
一个新值
应用于您的代码
的解决方案文件common.py
:
import time
X = {}
def set():
global X
X[time.time()] = 1
def get():
global X
return X
文件a.py
:
import common
import time
import b
if __name__ == '__main__':
b.monitor()
while True:
time.sleep(1)
common.set()
文件b.py
:
import common
import threading
import time
def f():
while True:
time.sleep(1)
print common.get(), 'in b'
def monitor():
t = threading.Thread(target=f)
t.setDaemon(True)
t.start()
a.py
import time
import b
X = {}
def set():
# global X
X[time.time()] = 1
def get():
global X
return X
if __name__ == '__main__':
b.monitor()
while True:
time.sleep(1)
b.py
import threading
import time
import a
def f():
while True:
time.sleep(1)
a.set()
print a.get(), 'in b'
def monitor():
t = threading.Thread(target=f)
t.setDaemon(True)
t.start()
最后是您的答案:
{1518083438.771415: 1} in b
{1518083438.771415: 1, 1518083439.772717: 1} in b
{1518083440.773916: 1, 1518083438.771415: 1, 1518083439.772717: 1} in b
我认为答案在于python并不真正允许全局变量。全局的范围通常仅在同一模块中。因此,当另一个模块导入具有全球的模块时,将复制名称空间,并创建具有相同原始值的变量。导入的Python模块将无法直接访问导入该模块中的全球模块,反之亦然。
您可能需要阅读第1.2.5节&1.15.6来自诺曼·马特洛夫(Norman Matloff(的快速而无痛的python pdf教程