【问题标题】:Dynamic subclassing with names from list of strings in Python使用 Python 中字符串列表中的名称进行动态子类化
【发布时间】:2025-12-05 19:55:02
【问题描述】:

我想使用字符串作为子类名称从超类动态创建子类。例如,假设我有一个代表 HTML 标记的类。

class Tag:
   def __init__(self, content, **attr):
       pass
   def __repr__(self):
       return f"<{self.__class__.__name__.lower()}>{content}</{<self.__class__.__name__.lower()>}>"

我也有这个清单:

class_names = ["Div", "A", "Body", "Html", "Nav"] # etc. Basically contains all html tags.

我想以某种方式创建 Tag 类的子类(可能使用 for 循环),即我想要新的类,即 Div, A, Body, Html, Nav, etc.,它们都是 Tag 类的子类。有没有快速的方法来做到这一点?不显式声明一个类也是不好的做法吗?

编辑:我想创建子类,不是对象。

编辑2:正如Goyo所说,我的目的并不明确,我想基本上一口气实现所有html标签类。很多人已经实现了 HTML 生成器,但是对于 this example,他们必须定义 Tag 类的所有子类,并在子类中写 pass,如下所示:

...

class Address(HtmlTag):
    """Defines contact information for the author/owner of a document"""
    pass


class Applet(HtmlTag):
    """Not supported in HTML5. Use <embed> or <object> instead."""
    pass


class Area(SelfClosingHtmlTag):
    """Defines an area inside an image-map"""
    pass
...

这是我想要避免的,因为这对我来说看起来像是多余的重复代码和大量的复制粘贴。

编辑 3:我很抱歉提到其他人的代码作为 “坏” 示例。无论如何,这不是我的意图。我只是想学习如何编写更短更简洁的代码。

【问题讨论】:

标签: python inheritance subclass


【解决方案1】:

查看type 的文档:

类型名称碱基dict
[...]
使用三个参数,返回一个新的类型对象。这本质上是类语句的动态形式。 name 字符串是类名,成为__name__ 属性;基元组逐项列出基类并成为__bases__ 属性; dict 字典是包含类主体定义的命名空间,并被复制到标准字典中以成为__dict__ 属性。

所以这样的东西应该创建你想要的类:

type(class_name, (Tag,), {})

如果您想为这些类创建别名,这里已经多次询问和回答。是almost always a bad idea,但既然你坚持,那就是:

class Tag:
   def __init__(self, content, **attr):
       self.content = content

   def __repr__(self):
       return f"<{self.__class__.__name__.lower()}>{self.content}</{self.__class__.__name__.lower()}>"

class_names = ["Div", "A", "Body", "Html", "Nav"]

for class_name in class_names:
    globals()[class_name] = type(class_name, (Tag,), {})

div = Div('some content')
print(div)

不过,这段代码有几个问题:

很难读懂

对于普通读者来说,DivA 等类的存在或它们的作用并不明显。至少不像静态定义那样明显。

旨在“一次性”完成几件事并编写“更短、更简洁的代码”通常会损害可读性和可维护性。 Remember 显式优于隐式,稀疏优于密集和可读性。

使用临时字典可能会有所改进,但不能完全解决问题。

tag_classes = {class_name: type(class_name, (Tag,), {})
               for class_name in class_names}
div = tag_clases['Div']('some content')
print(div)

你正在做一个没有区别的区别

您正在创建一堆行为相同的类。重点是什么?通过动态地创建它们,您甚至会失去静态类型检查的潜在好处。 Div 的实例和A 的实例之间的唯一区别是您在类名中编码的一些数据。通过将这些数据存储在实例属性中,您可以省去所有这些类的需要,并且代码变得更加简单。

class Tag:
    def __init__(self, tag_name, content, **attr):
        self.tag_name = tag_name
        self.content = content

    def __repr__(self):
       return f"<{self.tag_name.lower()}>{self.content}</{self.tag_name.lower()}>" 

div = Tag('Div', 'some_content')
print(div)

【讨论】:

  • 这并不能真正回答我的问题,因为尽管设置了 __name__ 属性,但我无法使用该名称实例化类,例如我不能这样做Div("Some text", class_="my_class)。我已经对我的问题添加了一个编辑,解释了我为什么想要这个。
  • @MikePham 我编辑了答案以添加该内容,但我认为您试图使其过于复杂。