Python - 全局变量和 class 方法

Python - Global variables and class methods

好的,所以我对这个几乎失去了理智。

我正在开发一个小应用程序,它从输入 XML 文件中获取问题,将它们显示在屏幕上,获取答案并将其写回输出 XML,使用 tkinter XML 处理的 GUI 和 ElementTree。 每个问题可能有多个检查和子问题,它们会根据需要动态显示在各自的框架上。

主要 window 由 main 创建为 MainWindow class 实例(包含所有 tkinter 内容和一堆有用的方法),在代码的最后:

def main():
    global app
    window = Tk()
    app = MainWindow(window)
    window.mainloop()

if __name__ == '__main__':
    main()

这应该使 'app' 成为一个全局的,应该可以从代码的任何地方访问,对吗?

.....好像不是?

我创建了问题 class,它存储从 XML 中提取的属性,处理文本的显示并为 class 中的每个元素创建检查 class =103=],以及 XML.

中每个元素的子问题 class

题目class如下:

class Question:
    def __init__(self, element):
        self.element = element
        self.question_text = element.get('text')
        self.ok = element.get('ok')
        self.answer = element.get('r')
        self.comment = element.get('comment')
        
        if element.find('check') is None:
            #print(app)
            pass
        else:
            #print(app)
            for check in element.iter('check'):
                Check(check)
            
        for subq in element.iter('subquest'):
            SubQuestion(subq)

还有 Check class,它处理复选框的创建以及将它们各自的答案写入 ElementTree:

class Check:
    def __init__(self, element):
        self.element = element
        self.check_text = element.get('text')
        self.answer = element.get('r')
        self.var = IntVar()
        
        self.create_check()
    
    def create_check(self):
        print('check created')
        print(app)
        Checkbutton(app.frameChk, text=self.check_text, variable=self.var, command=self.write_check).pack(side=LEFT)

    def write_check(self):
        self.element.set('r', self.var.get())
        print('check written')

到目前为止,还不错。它按预期工作,出现了复选框并提交了它们的值,没有问题

但是...您在创建复选框时是否注意到 Check class 引用 app.frameChk 中的 create_check 方法?这是为复选框保留的 Tk 框架,由 MainWindow 的 __init__ 方法创建。这个 create_check 方法访问 app 没有任何问题,即使其中没​​有 global 关键字。我什至添加了 print(app) 只是为了确定。

果然,每次创建复选框时,它都会将 <__main__.MainWindow object at 0x0000024CCC557BE0>" 打印到控制台。

但是...在问题的 __init__ 方法(或其他任何地方)中,我似乎无法访问与 app 相关的任何内容,它会抛出 classes_app.py", line 129, in __init__ - print(app) NameError: name 'app' is not defined

到底为什么?这两个 classes 没有从其他人那里继承任何东西,并且在层次上是 'equals',只是程序中的两个平均 classes(实际上,Question 是创建 Check 实例的那个!) .一个人认识全球而其他人不认识?

最令人难以置信的是:Check class 中没有 global 关键字,它仍然有效?

到目前为止我已经尝试过:

1 - 在全局范围内的 main 函数之外声明 app = MainWindow(window)。没有。

2 - 在程序的每个函数中使用 global 关键字来查看他们是否得到它。没有。

3 - 将 main 函数放在代码的最顶部,这样它就是第一个 app 的声明似乎也没什么作用。

4 - 上述解决方案和反复试验恶作剧的任意组合。

5 - 牺牲一块主板给python globals 的代码女神。

请注意,我是 python 的新手,所以我可能对全局变量的理解非常错误,或者完全不理解,但根据文档所说,用 global 关键字应该是一个全局变量,并且可以从代码中的所有函数和方法访问全局变量,对吗?

如果你问为什么我如此需要它来让 app = MainWindow(window) 成为全局的:会有一个全局函数来清理每个问题之间的小部件:

def destroy_widgets(parent):
    for widget in parent.winfo_children():
        widget.destroy()

所以在 classes 的方法中,我会像 destroy_widgets(app.frameChk) 那样称呼它。这是我第一次遇到这个问题。

我会 post 整个代码,但是所有这些 tkinter 定义都非常广泛。如果你们认为有必要,我当然会。

任何帮助或见解将不胜感激,在此先感谢,这个社区是黄金!

Definition/lifetime 个变量
关于这个:

Most mind-boggling: the Check class does NOT have the global keyword in it, and it still works?

您只需要 global 关键字来在本地上下文中创建或更改全局变量,see this answer

此外,语句 global app 尚未创建变量 app - 它仅在第一次赋值时创建。在您的示例中,这意味着 app 仅在此处创建:app = MainWindow(window)。 如果 Question 在此语句之前创建(在您的示例中,通过 Tk()MainWindow(window)),则 app 尚不存在。

你可以在解释器中试试这个:

>>> global app
>>> print(app)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'app' is not defined

命名空间

另一个需要注意的重要事实:如果您的代码组织在不同的模块中,您必须注意命名空间。

例子 假设我们有两个 python 文件:first.pysecond.py:

first.py的内容:

app = 5

second.py的内容:

import first
print(first.app)

运行 这个有效:

$ python3 second.py
5

只放 print(app) 是行不通的,因为模块不同。