Python 使用eval()动态生成Tkinter子顶级类

Python 使用eval()动态生成Tkinter子顶级类,python,tkinter,Python,Tkinter,我想知道这个例子是否足以作为在python中使用备受诟病的eval()函数的借口 我制作了一个选择弹出窗口的基类。因为这个循环在我的程序中是一个很常见的主题,所以我从中继承它来创建各种选择窗口。机场、飞机、人员等。。。我只是更改它显示的信息和用于查询数据库的查询,我将其作为变量传入(为了简化,这里不显示) 正如您在下面看到的,我传入了一个“返回变量”的名称,该变量将在确认选择并成功关闭子窗口时更新。我尝试过setattr(),但我无法摆脱它是一个字符串并抛出一个错误,所以我一直坚持使用eval(

我想知道这个例子是否足以作为在python中使用备受诟病的eval()函数的借口

我制作了一个选择弹出窗口的基类。因为这个循环在我的程序中是一个很常见的主题,所以我从中继承它来创建各种选择窗口。机场、飞机、人员等。。。我只是更改它显示的信息和用于查询数据库的查询,我将其作为变量传入(为了简化,这里不显示)

正如您在下面看到的,我传入了一个“返回变量”的名称,该变量将在确认选择并成功关闭子窗口时更新。我尝试过setattr(),但我无法摆脱它是一个字符串并抛出一个错误,所以我一直坚持使用eval()函数

#!/usr/bin/env python3

import tkinter as tk


class Application(tk.Frame):
    
    def __init__(self, master = None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack()
        self.config(width = 400)
        self.create_application()
    
    def create_application(self):
        self.label1 = tk.Label(self, text = "Label 1")
        self.label1.pack()
        self.entry1_string = tk.StringVar()
        self.entry1 = tk.Entry(self, state = "readonly",
                                textvariable = self.entry1_string)
        self.entry1.pack()
        self.entry1_select = tk.Button(self, text = "Select...",
                          command = lambda: self.create_entry1_select_window())
        self.entry1_select.pack()
        self.label2 = tk.Label(self, text = "Label 2")
        self.label2.pack()
        self.entry2_string = tk.StringVar()
        self.entry2 = tk.Entry(self, state = "readonly",
                                textvariable = self.entry2_string)
        self.entry2.pack()
        self.entry2_select = tk.Button(self, text = "Select...",
                          command = lambda: self.create_entry2_select_window())
        self.entry2_select.pack()
        
    def create_entry1_select_window(self):
        self.entry1_select_window = AirportSelectWindow(self,
                                             return_variable = "entry1_string")
        self.entry1_select_window.title("Select Entry 1")
        
    def create_entry2_select_window(self):
        self.entry2_select_window = AirportSelectWindow(self,
                                             return_variable = "entry2_string")
        self.entry2_select_window.title("Select Entry 2")

class BaseSelectWindow(tk.Toplevel):
    
    def __init__(self, master = None, return_variable = None, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.configure(padx = 10, pady = 10)
        self.master = master
        ######### Is this ok?
        self.return_variable_path = eval("self.master."+return_variable)
        ## Just for example. Real thing retrieves values from DB. ##
        self.search_results = ["Heathrow", "O'Hare", "LAX", "Schipol"]
        self.create_base_widgets()
    
    def create_base_widgets(self):
        self.selection_string = tk.StringVar()
        self.selection_data = tk.OptionMenu(self, self.selection_string,
                                            *self.search_results)
        self.selection_data.pack()
        self.selection_button = tk.Button(self, text = "Confirm...",
               command = lambda: self.confirm_selection(self.selection_string))
        self.selection_button.pack()
    
    def confirm_selection(self, selection):
        self.return_variable_path.set(selection.get())
        self.destroy()
        

class AirportSelectWindow(BaseSelectWindow):
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

def main():
    root = tk.Tk()
    root.title('Main Window')

    app = Application(root)
    app.mainloop()

if __name__ == "__main__":
    main()

这很酷吗?它不是用户生成的值,因此应该是安全的。

不,它不是
eval
的好用法。在这种情况下,
eval
的唯一优点是使代码更难理解

显然,您希望将属性的名称发送给
AirportSelectWindow
的构造函数,但是如果您只传入实际变量,代码将更容易理解

class BaseSelectWindow(tk.Toplevel):

    def __init__(self, master = None, return_variable = None, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.return_variable_path=return_variable
        ...
然后,您可以这样创建它:

def create_entry1_select_window(self):
    self.entry1_select_window = AirportSelectWindow(self,
                                         return_variable = self.entry1_string)

但是,我将使用
self.return\u variable
而不是
self.return\u variable
。我不认为路径增加了清晰度,事实上消除了一些清晰度。我还将
self.entry1\u string
更改为
self.entry1\u var
,因为变量表示
StringVar
,而不是字符串(entry2也是如此)。

不,这不是
eval
的好用法。在这种情况下,
eval
的唯一优点是使代码更难理解

显然,您希望将属性的名称发送给
AirportSelectWindow
的构造函数,但是如果您只传入实际变量,代码将更容易理解

class BaseSelectWindow(tk.Toplevel):

    def __init__(self, master = None, return_variable = None, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.return_variable_path=return_variable
        ...
然后,您可以这样创建它:

def create_entry1_select_window(self):
    self.entry1_select_window = AirportSelectWindow(self,
                                         return_variable = self.entry1_string)

但是,我将使用
self.return\u variable
而不是
self.return\u variable
。我不认为路径增加了清晰度,事实上消除了一些清晰度。我还想将
self.entry1\u string
更改为
self.entry1\u var
,因为变量表示
StringVar
,而不是字符串(entry2也是如此).

为什么传递的字符串是属性名而不是实际属性?上面的代码段比我正在开发的实际代码要短得多。这是一个符合SO发布小型工作示例的政策的示例。我会尝试一下你下面的建议,然后再回来找你。我问的问题与上述代码无关,更多关于传递预期返回变量名的原理,以便我将来可以以相同的方式重用基类并节省代码。为什么要传递属性名而不是实际属性的字符串?上面的代码段比我正在开发的实际代码要短得多。这是一个符合SO发布小型工作示例的政策的示例。我会尝试一下你下面的建议,然后再回来找你。我问的问题与上面的代码无关,更多的是关于传递预期返回变量名的原理,这样我将来可以以同样的方式重用基类并节省代码。好的,你教了我一些东西。我不知道你可以穿过一个物体,它会“跟踪”它。我原以为当您传入“self.entry1_string”时,子窗口会尝试逐字调用它,您会得到一个“child object has not attribute'entry1_string'”错误,因此我尝试使用“self.master.*.widget*”调用它。我已经做了你建议的修改,效果很好,非常感谢。嗨,布莱恩,你能帮我更好地理解这一点吗?从python文档中我所记得的,我想我可以理解,当创建子窗口时,我是否正确地认为创建了一个新的名称空间?那么,当您将小部件传入返回变量时,它是否隐式地称为“self.master.*widget”?对象是否在两个名称空间中存在两个不同的引用,或者子窗口是否包含一个引用,有点像C中的指针?@pilky01:No,当您传入一个返回变量时,它不会隐式调用任何内容。在我的示例中,我显式地将参数命名为
return\u variable
,并显式地将其保存为
self.return\u variable\u path
。这些是参考资料。只有一个对象,有多个引用。谢谢你的回答。好的,你教会了我一些东西。我不知道你可以穿过一个物体,它会“跟踪”它。我原以为当您传入“self.entry1_string”时,子窗口会尝试逐字调用它,您会得到一个“child object has not attribute'entry1_string'”错误,因此我尝试使用“self.master.*.widget*”调用它。我已经做了你建议的修改,效果很好,非常感谢。嗨,布莱恩,你能帮我更好地理解这一点吗?从python文档中我所记得的,我想我可以理解,当创建子窗口时,我是否正确地认为创建了一个新的名称空间?那么,当您将小部件传入返回变量时,它是否隐式地称为“self.master.*widget”?对象是否在两个名称空间中存在两个不同的引用,或者子窗口是否包含一个引用,有点像C中的指针?@pilky01:No,当您传入一个返回变量时,它不会隐式调用任何内容。在我的示例中,我显式地将参数命名为
return\u variable
,并显式地将其保存为
self.return\u variable\u path
。这些是参考资料。只有一个对象,有多个引用。谢谢