这个Python类变量作用域行为是完全意想不到的,而且很奇怪。这是怎么回事?

问题描述:

好吧,这其中有我撕裂我的头发:这个Python类变量作用域行为是完全意想不到的,而且很奇怪。这是怎么回事?

我有一个多进程的程序,有独立的工人在每一个给定的任务工作。

当KeyboardInterrupt出现时,我希望每个工作人员将其内部状态保存到一个文件中,以便它可以继续下一次停止的位置。

无论其...

它看起来像它包含关于在此之前可能发生的状态消失信息字典!

怎么样? exit()函数正在访问更多的全局范围版本的字典......事实证明,各种run()(和从属于run())函数已经创建了它们自己的变量版本。

没什么奇怪有关...

除...

他们都已经使用了自我。关键词。

其中,如果我的理解是正确的,应该意味着他们总是访问变量的实例范围版本...不创建它们自己的!

下面的代码的简化版本:

import multiprocessing 
import atexit 
import signal 
import sys 
import json 

class Worker(multiprocessing.Process): 
    def __init__(self, my_string_1, my_string_2): 
     # Inherit the __init_ from Process, very important or we will get errors 
     super(Worker, self).__init__() 
     # Make sure we know what to do when called to exit 
     atexit.register(self.exit) 
     signal.signal(signal.SIGTERM, self.exit) 
     self.my_dictionary = { 
      'my_string_1' : my_string_1, 
      'my_string_2' : my_string_2 
      } 
    def run(self): 
     self.my_dictionary = { 
      'new_string' : 'Watch me make weird stuff happen!' 
      } 
     try: 
      while True: 
       print(self.my_dictionary['my_string_1'] + " " + self.my_dictionary['my_string_2']) 
     except (KeyboardInterrupt, SystemExit): 
      self.exit() 
    def exit(self): 
     # Write the relevant data to file 
     info_for_file = { 
      'my_dictionary': self.my_dictionary 
      } 
     print(info_for_file) # For easier debugging 
     save_file = open('save.log', 'w') 
     json.dump(info_for_file, save_file) 
     save_file.close() 
     # Exit 
     sys.exit() 

if __name__ == '__main__': 
    strings_list = ["Hello", "World", "Ehlo", "Wrld"] 
    instances = [] 
    try: 
     for i in range(len(strings_list) - 2): 
      my_string_1 = strings_list[i] 
      my_string_2 = strings_list[i + 1] 
      instance = Worker(my_string_1, my_string_2) 
      instances.append(instance) 
      instance.start() 
     for instance in instances: 
      instance.join() 
    except (KeyboardInterrupt, SystemExit): 
     for instance in instances: 
      instance.exit() 
      instance.close() 

上运行,我们得到以下回溯...

Process Worker-2: 
Process Worker-1: 
Traceback (most recent call last): 
    File "/usr/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap 
    self.run() 
Traceback (most recent call last): 
    File "/usr/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap 
    self.run() 
    File "<stdin>", line 18, in run 
    File "<stdin>", line 18, in run 
KeyError: 'my_string_1' 
KeyError: 'my_string_1' 

换句话说,即使在被明确添加的关键my_string_1 init,run()函数正在访问self.my_dictionary的新版本,该版本不包含该密钥!

同样,如果我们用一个普通变量处理(my_dictionary而不是self.my_dictionary),但我认为self.variables总是实例范围,这将有望...

这到底是怎么回事?

你的问题基本上可以由以下表示:

class Test: 
    def __init__(self): 
     self.x = 1 

    def run(self): 
     self.x = 2 

     if self.x != 1: 
      print("self.x isn't 1!") 

t = Test() 
t.run() 

注意什么run在做什么。

您有不兼容的数据覆盖您的实例成员self.my_dictionary当你写

self.my_dictionary = { 
      'new_string' : 'Watch me make weird stuff happen!' 
} 

然后尝试使用不兼容的数据时,你说

print(self.my_dictionary['my_string_1']... 

这不正是明确你的目的是什么,当你覆盖my_dictionary,但这就是为什么你会收到错误。你需要重新思考你的逻辑。

+0

谢谢,我想你明白了,而且你是正确的,我的代码本来可以更小。我不确定多处理方面是否可能是问题的一部分。 我的意思是设置self.my_dictionary [“new_string”] ='看我做出奇怪的事情发生!' ... 在原始演示代码中进行修正会产生预期结果。 不幸的是,在原始程序中进行相同的修正不会!我仍然得到一个'schitzophrenic变量'的效果,当程序退出时,数据运行()添加到字典中不存在... – right2clicky

+0

@ right2clicky您必须在别处做类似的事情。简而言之,你所描述的效果不仅仅发生在自己身上。尝试找出演示不同的地方。 – Carcigenicate

+0

嗯,我搜索了self.my_dictionary的等效变量的代码,以下是我的操作: __init__:初始化为{} ... body函数:len()... body函数:。 append(somedata)... body function:.pop(index)... body function:.extend(somedata)... ...以及从变量中获取数据的一些事情。而已! 我不知道这些如何会打破或覆盖它... – right2clicky