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

时间:2018-02-08 09:01:53

标签: python multithreading global-variables

我有两个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未在[{1}}中更新,即使调用set正在向其添加这些密钥?

3 个答案:

答案 0 :(得分:2)

问题是在这样的设置中创建了变量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 documentation以获取有关如何解析模块名称的说明。

因此,这两个模块中的名称是截然不同的; import a不会使__main__名称可供其他模块访问。

解决方案

快速解决方案是使用import a语句替换b.py中的import __main__语句,并使用__main__.X作为您要在{{1}中访问的限定名称}}。但这不被视为advised way to proceed

更健壮的方法是确保全局变量不是在执行的脚本中定义,而是在导入的模块中定义 - 由导入的模块 b.py和{{1 }}。第二个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原值
  B新值
  新值

应用于您的代码的解决方案

档案b.py

import common
def monitor():
    print ('B', common.X)
    common.X = "new value"
    print ('B', common.X)

档案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()

答案 1 :(得分:0)

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

答案 2 :(得分:0)

我认为答案在于python并不真正允许全局变量。全局范围通常仅在同一模块中。因此,当另一个模块导入具有全局变量的模块时,将复制名称空间并创建具有相同原始值的变量。导入的Python模块不能直接访问导入它的模块中的全局变量,反之亦然。

您可能需要阅读1.2.5&部分。 1.15.6来自Norman Matloff的Quick and Painless Python PDF Tutorial