在 Python (tkinter) 中从外部更改 Class 的私有属性(标签)

Changing private attributes (labels) of a Class from outside of it in Python (tkinter)

我在 Python 中使用 tkinter 制作了一个 GUI,我创建了一个名为“Window”的 Class 来管理整个会话期间 GUI 中的所有创建、运行和更改。 我不知道我是不是把它弄得太复杂了,但我正在尝试构建一种方法,这样我只能从“Window”class 中更改标签,因为有很多标签,我想制作类似下一个代码的东西(这是一个最小的可重现示例):

from tkinter import *

class Window:
    def __init__(self, wind):
        self.__window = wind
        self.__window.geometry("200x100")
        self.__window.title("Title")

        self.__label_1 = Label(self.__window, text="Label 1 text")
        self.__label_1.pack()

        self.__button_1 = Button(self.__window, text="Change", command=function)
        self.__button_1.pack()

    def change_label(self, label, text): #this 'label' is the name of the label I want to change
        self.label["text"] = text        #here I try to use it as the attibute's name


def function():
    myWindow.change_label("__label_1", "New text")


if __name__=='__main__':
    wind = Tk()
    myWindow = Window(wind)
    wind.mainloop()

我面临的问题显然是:AttributeError,'Window'对象没有属性'label'。

有没有办法制作我想制作的东西?将我要更改的标签的名称发送到方法,接收它并将其视为属性名称?

或者我应该只将标签声明为 public 并从 class“Window”之外更改它?

问题是标签很多,不想被误改,所以想“简化”改的过程,但可能是我弄得太复杂了。

提前感谢您的回复。

这里有两个基本问题。 Python 没有私有属性。 Python 没有 any 访问修饰符。您在这里使用的是双下划线名称修改。那就是和private一样。它只是简单地将名称改造成 _MyClass__my_attribute 以防止子类中的意外名称冲突,它仍然可以从外部完全访问:

>>> class Foo:
...     def __init__(self):
...         self.__bar = 42
...
>>> foo = Foo()
>>> foo._Foo__bar
42

表示不属于 public API 的变量的惯用方法是使用 单下划线 ,例如self._bar = 42,这可能是您 应该在这里使用的

现在,这个方法永远行不通了:

def change_label(self, label, text): #this 'label' is the name of the label I want to change
    self.label["text"] = text        #here I try to use it as the attibute's name

随心所欲。要使用字符串修改属性,请使用 getattrsetattr:

>>> class Foo: pass
...
>>> foo = Foo()
>>> setattr(foo, 'bar', 42)
>>> foo.bar
42
>>> getattr(foo, 'bar')
42

如果您想使用字符串访问小部件,您需要通过 name 选项提供一个唯一的名称。为了更容易访问,最好使用字典来存储小部件,使用它们的名称作为键。

下面是基于您的修改示例:

from tkinter import *

class Window:
    def __init__(self, wind):
        self.__window = wind
        self.__window.geometry("200x100")
        self.__window.title("Title")

        self.__widgets = {} # dictionary to hold the created widgets

        # use whatever "name" you want but it must be unique
        self.create(Label, text="Label 1 text", name="label_1")
        self.create(Button, text="Change", command=function, name="button_change")

    def create(self, widget, **kw):
        w = widget(self.__window, **kw)
        w.pack()
        name = kw.get("name", str(w)) # use implicitly created name if "name" option is not given
        self.__widgets[name] = w
        return name

    def change(self, name, **kw):
        self.__widgets[name].config(**kw)


def function():
    # access the widget using the unique name
    myWindow.change("label_1", text="New text", font="Arial 24 bold")


if __name__=='__main__':
    wind = Tk()
    myWindow = Window(wind)
    wind.mainloop()