跨多个模块和线程访问全局变量



我有两个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都应被引用。那么,为什么Xb.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.pyb.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教程

相关内容

  • 没有找到相关文章

最新更新