使用Python字符串列表中的名称进行动态子类化

时间:2019-07-01 15:25:04

标签: python inheritance subclass

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

suppressWarnings

我也有这个列表:

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

我想以某种方式创建Tag类的子类(也许使用for循环),即我想拥有新的类class_names = ["Div", "A", "Body", "Html", "Nav"] # etc. Basically contains all html tags. ,它们都是Tag类的子类。有快速的方法吗?不明确声明类也是不好的做法吗?

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

编辑2:正如Goyo所说,我的目的尚不清楚,我想一次实现所有html标记类。许多人已经实现了HTML生成器,但是对于this example,他们必须定义Tag类的所有子类,然后只需在子类中编写Div, A, Body, Html, Nav, etc.,如下所示:

pass

这是我要避免的事情,因为这看起来像是重复的冗余代码,并且对我来说很多复制粘贴。

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

1 个答案:

答案 0 :(得分:1)

查看type的文档:

  

类型名称碱基 dict
  [...]
  具有三个参数,返回一个新的类型对象。这本质上是类语句的动态形式。名称字符串是类名称,并成为__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实例之间的唯一区别是您要在类名中编写一些数据。通过将数据存储在instance属性中,您可以避免所有这些类的需要,并且代码变得更加简单。

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)