Python 如何使用exec()函数正确创建类的实例?

Python 如何使用exec()函数正确创建类的实例?,python,python-3.x,tkinter,class-method,Python,Python 3.x,Tkinter,Class Method,我正在使用Python中的TKinter库制作GUI。我希望用户从组合框中选择一个选项,然后按下一个按钮,该按钮将创建一个名为selected option的类的实例。为了保存代码,我决定以以下方式使用exec()功能: exec('instance='+comboExample.get()+'()')。 这将启动类的\uuuu init\uuuuu()方法,但当我尝试使用instance.method()调用其他方法(在本例中是从继承的类)时,它会显示以下错误:NameError:未定义名称“

我正在使用Python中的TKinter库制作GUI。我希望用户从组合框中选择一个选项,然后按下一个按钮,该按钮将创建一个名为selected option的类的实例。为了保存代码,我决定以以下方式使用
exec()
功能:
exec('instance='+comboExample.get()+'()')
。 这将启动类的
\uuuu init\uuuuu()
方法,但当我尝试使用
instance.method()
调用其他方法(在本例中是从继承的类)时,它会显示以下错误:
NameError:未定义名称“instance”
。这里有一个脚本示例:

from tkinter import *
from tkinter import ttk

master = Tk()

#Create classes
class Base():
    def method(self):
        self.label = Label(master, text = self.sentence)
        self.label.pack()

class Example1(Base):
    def __init__(self):
        print('Example1 created')
        self.sentence = 'This is example 1.'

class Example2(Base):
    def __init__(self):
        print('Example2 created')
        self.sentence = 'This is example 2'

#Create Combobox and Button
combo = ttk.Combobox(master, state = 'readonly')
combo['values'] = ['Example1', 'Example2']
combo.pack()

def callback():
    exec('instance = ' + combo.get() + '()')
    #Here is the error
    instance.method()

button = Button(master, command = callback, text = 'Button')
button.pack()

master.mainloop()
我现在不知道为什么,但当我尝试使用以下代码时,它会正常工作:

class Example():
    def __init__(self):
        self.text = 'This is an example'
    def add_text(self):
        print(self.text)

exec('instance = Example()')
instance.add_text()
目前,我只找到了一个解决方案,即不使用
exec()
,但这会使我比使用它浪费更多的代码,特别是如果我想创建很多类,如
Example1
Example2
。这与上一个大脚本非常相似,但是更改了
callback()
函数:

def callback():
    if combo.get() == 'Example1':
        instance = Example1()
    if combo.get() == 'Example2':
        instance = Example2()
    instance.method()
就这些。我两个月前才开始用Python编程,我也是stackoverflow的新手,所以如果我在解释或其他方面犯了一些错误,请告诉我,我会修复它。
谢谢你抽出时间。任何帮助都将不胜感激。

您不应为此目的使用
exec
exec
是一个功能强大的工具,但它不适合此工作

一种更简单的方法是创建从用户输入到类的映射。然后,您可以将该映射用于组合框和回调

例如:

...
mapping = {"Example1": Example1, "Example2": Example2}

#Create Combobox and Button
combo = ttk.Combobox(master, state = 'readonly')
combo['values'] = sorted(mapping.keys())
combo.pack()

def callback():
    class_name = combo.get()
    cls = mapping[class_name]
    instance = cls()
    instance.method()
...

您甚至可以通过迭代类列表来自动生成映射,尽管在本例中这似乎有些过分。

您不应该为此使用
exec
exec
是一个功能强大的工具,但它不适合此工作

一种更简单的方法是创建从用户输入到类的映射。然后,您可以将该映射用于组合框和回调

例如:

...
mapping = {"Example1": Example1, "Example2": Example2}

#Create Combobox and Button
combo = ttk.Combobox(master, state = 'readonly')
combo['values'] = sorted(mapping.keys())
combo.pack()

def callback():
    class_name = combo.get()
    cls = mapping[class_name]
    instance = cls()
    instance.method()
...

您甚至可以通过迭代一系列类来自动生成映射,不过在本例中,这似乎有些过头了。

问题不在于语法;这是因为你试图做一些非法的事情。不能使用
exec
创建新的局部变量。(函数外的相同代码工作的原因是,通常可以使用
exec
创建新的全局变量,但这仍然是一个坏主意。)

但你也不需要这么做。在Python中,一切都是对象,包括类。因此,您只需要从名称中获取类。然后,您可以创建该类的实例,并将其存储在局部变量中,方法是使用静态实例化类并将其存储在局部变量中所使用的相同常规语法


正确的方法是存储将名称映射到类对象的字典。如果你想变得聪明,你可以编写一个decorator,在字典中注册类,但是如果你觉得这听起来像希腊语,那么就显式地这样做:

classes = {'Spam': Spam, 'Eggs': Eggs}
如果你有几十个,你可以通过这样的理解避免重复:

classes = {cls.__name__: cls for cls in ('Spam', 'Eggs')}
…但在这一点上,你最好学习如何编写装饰程序

无论哪种方式,您都可以用该字典的键填充组合框,而不必在
combo['values']
行中重复自己的操作

然后,要创建实例,只需执行以下操作:

cls = classes[comboExample.get()]
instance = cls()
(很明显,你可以把它折叠成一行,但我认为如果我们把这两部分分开,会更容易理解。)



如果你真的想用黑客的方式做这件事,你可以。在此模块中创建的每个类都已按模块的全局命名空间的名称存储在字典中。这与您尝试使用
exec
隐式查找它的位置相同,但您只需在
globals()
中查找它即可显式查找它。但是,全局名称空间还包含所有函数、导入的模块、顶级常量和变量等的名称,因此这通常不是一个好主意。(显然,
exec
也有同样的问题。)

问题不在于你的语法;这是因为你试图做一些非法的事情。不能使用
exec
创建新的局部变量。(函数外的相同代码工作的原因是,通常可以使用
exec
创建新的全局变量,但这仍然是一个坏主意。)

但你也不需要这么做。在Python中,一切都是对象,包括类。因此,您只需要从名称中获取类。然后,您可以创建该类的实例,并将其存储在局部变量中,方法是使用静态实例化类并将其存储在局部变量中所使用的相同常规语法


正确的方法是存储将名称映射到类对象的字典。如果你想变得聪明,你可以编写一个decorator,在字典中注册类,但是如果你觉得这听起来像希腊语,那么就显式地这样做:

classes = {'Spam': Spam, 'Eggs': Eggs}
如果你有几十个,你可以通过这样的理解避免重复:

classes = {cls.__name__: cls for cls in ('Spam', 'Eggs')}
…但在这一点上,你最好学习如何编写装饰程序

无论哪种方式,您都可以用该字典的键填充组合框,而不必在
combo['values']
行中重复自己的操作

然后,要创建实例,只需执行以下操作:

cls = classes[comboExample.get()]
instance = cls()
(很明显,你可以把它折叠成一行,但我认为如果我们把这两部分分开,会更容易理解。)


如果你真的想用黑客的方式做这件事,你可以。您在此模块中创建的每个类都已按名称存储在字典中