从字符串列表构建类名

0 投票
1 回答
689 浏览
提问于 2025-04-18 15:55

我在 Tkinter 中创建了 FrameLabel 的子类,这样可以自动调用 .pack() 方法(这只是一个使用示例,我的问题并不严格与 Tkinter 相关)。这两个类的定义几乎是一样的,除了标签部分:

class Frame(tk.Frame):
    def __init__(self, parent, **kwargs):
        objparams, packparams = dispatch_parameters(self, **kwargs)
        tk.Frame.__init__(self, parent, objparams)
        self.pack(packparams)

class Label(tk.Label):
    def __init__(self, parent, **kwargs):
        objparams, packparams = dispatch_parameters(self, **kwargs)
        tk.Label.__init__(self, parent, objparams)
        self.pack(packparams)

为了避免在这两个类中重复相同的代码,我想知道如何通过仅改变“标签”来重用这些代码。我知道上面提到的 "Frame" 在类内部有不同的含义(比如类名、实际的类等等),所以我在尝试理解是否可以用类似于(这只是伪代码,试图解释我的观点)的方法来处理:

for classname in ["Frame", "Label"]:
  class <<classname>>(tk.<<classname>>):
    def __init__(self, parent, **kwargs):
        objparams, packparams = dispatch_parameters(self, **kwargs)
        tk.<<classname>>.__init__(self, parent, objparams)
        self.pack(packparams)

在这种代码重用的情况下,有没有更符合 Python 风格的方法?还是说我应该继续一个接一个地定义类,即使这些代码非常相似?

注意 1:我认为这个问题和 另一个问题非常相似,但涉及的是 Objective-C

注意 2:我故意省略了 tkinter 标签,因为我给出的例子只是一个一般情况的特定实例

1 个回答

1

你可以使用 type() 函数 动态创建类:

def class_factory(classname):
    base_class = getattr(tk, classname)

    def __init__(self, parent, **kwargs):
        objparams, packparams = dispatch_parameters(self, **kwargs)
        base_class.__init__(self, parent, objparams)
        self.pack(packparams)

    return type(classname, (base_class, object), {'__init__': __init__})

for classname in ["Frame", "Label"]:
    globals()[classname] = class_factory(classname)

当你给 type() 函数传入三个参数时,它就会根据这些参数生成一个类。第一个参数是类的名字,第二个参数是一个包含基类的元组,第三个参数是一个表示类内容的映射。

这里需要用到 object 这个基类,以便它能和 Tkinter 模块中的旧式类一起工作。

另一种选择是把 class 语句放在一个函数里:

def class_factory(classname):
    base_class = getattr(tk, classname)

    class CustomClass(base_class):
        def __init__(self, parent, **kwargs):
            objparams, packparams = dispatch_parameters(self, **kwargs)
            base_class.__init__(self, parent, objparams)
            self.pack(packparams)

    CustomClass.__name__ = classname
    return CustomClass

在这种情况下,我们就不需要混合使用 object 基类了。

撰写回答