Open4

python class

yg_siroyg_siro

自分が使うためだけのHTML DOMツリーを作成した。

class Node:
    def __init__(self, name: str):
        self.__name = name
        self.__attr = {}
        self.__child = []
        self.__level = 0

    # a[key] = value
    def __setitem__(self, key: str, value: str):
        self.__attr[key] = value

    # b = a[key]
    def __getitem__(self, key: str) -> str:
        return self.__attr.get(key, "")

    # key = "value"
    def __str_attr(self) -> str:
        a = [f'{k}="{v}"' if type(v) is str else k for k, v in self.__attr.items()]
        return " ".join(a)

    # この関数でlevelを指定すれば子要素のlevelも調整してくれる
    def __set_level(self, level: int):
        self.__level = level + 1
        for i in self.__child:
            if type(i) is Node:
                i.__set_level(self.__level)

    # 子要素の追加
    def append(self, node):
        """
        node: Node or str
        """
        if type(node) is Node:
            node.__set_level(self.__level)
        self.__child.append(node)

    def __str__(self) -> str:
        attr = self.__str_attr()
        if attr:
            attr = " " + attr
        level = "  " * self.__level

        if len(self.__child) > 0:
            # 子要素がテキストだけなら1行で表示
            if len(self.__child) == 1 and type(self.__child[0]) is str:
                return (
                    level + f"<{self.__name}{attr}>{self.__child[0]}</{self.__name}>\n"
                )
            else:
                result = level + f"<{self.__name}{attr}>\n"
                for i in self.__child:
                    result += str(i)
                result += level + f"</{self.__name}>\n"
                return result
        else:
            # 子要素がない場合
            return level + f"<{self.__name}{attr}/>\n"

使い方は以下の通り

html = Node("html")
html["lang"] = "ja" # 属性を指定する場合は左記のように
head = Node("head")
chara = Node("meta")
chara["charset"] = "UTF-8"
title = Node("title")
title.append("sample")
body = Node("body")
html.append(head)
head.append(chara)
head.append(title)
html.append(body)
script = Node("script")
script["src"] = "js/script.js"
script["defer"] = None # <script defer></script>のように属性に=を使用しない場合はNoneを指定する
script.append("") # scriptタグは終了タグも必要なので空文字を追加
head.append(script)
print(html)

実行結果

<html lang="ja">
  <head>
    <meta charset="UTF-8"/>
    <title>sample</title>
    <script src="js/script.js" defer></script>
  </head>
  <body/>
</html>
yg_siroyg_siro

Nodeを作成する時に属性も一緒に指定したい
キーワード変数は全て属性と考える?

class Node:
    def __init__(self, name: str, **kwargs): # kwargsを全て属性扱い
        self.__name = name
        self.__attr = kwargs
        .
        .

動作はするみたい

html = Node("html", lang="ja")
print(html)
result
<html lang="ja"/>
yg_siroyg_siro

TODO

  • 子要素をタグ名,ID,CLASSを指定して取得する関数
  • Emmetを引数に渡してツリーを作成する関数
  • DOCTYPE指定
  • コメントも追加できるといいね
yg_siroyg_siro

levelを中止、parentとrootを持たせた

class Node:
    def __init__(
        self, name: str, id: str | None = None, class_: str | None = None, **kwargs
    ):
        self.__name = name
        self.__attr = kwargs
        if id:
            self.__attr[id] = id
        if class_:
            self.__attr["class"] = class_
        self.__child = []
        self.__root = self
        self.__parent = None

    def __setitem__(self, key: str, value: str):
        self.__attr[key] = value

    def __getitem__(self, key: str) -> str:
        return self.__attr.get(key, "")

    def html_string(self, lank: int = 0) -> str:
        result = lank * "  " + f"<{self.__name}"
        for k, v in self.__attr.items():
            result += f' {k}="{v}"'
        if self.__child:
            result += ">\n"
            for i in self.__child:
                result += i.html_string(lank + 1)
            result += lank * "  " + f"</{self.__name}>\n"
        else:
            result += "/>\n"
        return result

    def append(self, node):
        if issubclass(type(node), Node):
            node.__root = self.__root
            node.__parent = self
            self.__child.append(node)


class TextNode(Node):
    def __init__(self, text: str):
        super().__init__("text")
        self.text = text
        self.__root = self
        self.__parent = None

    def __setitem__(self, key: str, value: str):
        pass

    def __getitem__(self, key: str) -> str:
        return ""

    def html_string(self, lank: int = 0) -> str:
        return lank * "  " + f"{self.text}\n"


a = Node("html", lang="ja")
b = Node("head")
c = Node("meta", charset="utf-8")
d = Node("title")
e = Node("body")
d.append(TextNode("Sample"))
a.append(b)
b.append(c)
b.append(d)
a.append(e)
print(a.html_string())

出力

<html lang="ja">
  <head>
    <meta charset="utf-8"/>
    <title>
      Sample
    </title>
  </head>
  <body/>
</html>