Skip to content

Instantly share code, notes, and snippets.

@CNSeniorious000
Last active February 13, 2023 05:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CNSeniorious000/cff233f439393dfada6464fd63bca5fe to your computer and use it in GitHub Desktop.
Save CNSeniorious000/cff233f439393dfada6464fd63bca5fe to your computer and use it in GitHub Desktop.
重载各种运算符,实现用原生的运算符生成树结构,并导出为xml字符串
<html>
<head>
<title>标题</title>
</head>
<body>
<img src="图片地址" alt="图片标题" />
<div>
<p>第0句</p>
<p>第1句</p>
<p>第2句</p>
<p>第3句</p>
<p>第4句</p>
<p>第5句</p>
<p>第6句</p>
<p>第7句</p>
<p>第8句</p>
<p>第9句</p>
</div>
<div>
<div>
<p>
<ul>
<li>还可以链式调用</li>
</ul>
</p>
</div>
</div>
</body>
</html>
from enum import IntEnum
class Line:
def __init__(self, indent_level: int = 0, indent_step: int = 2, indent_char: str = " "):
assert isinstance(indent_level, int)
assert isinstance(indent_step, int)
assert indent_level >= 0
assert indent_step >= 0
self._level = indent_level
self._step = indent_step
self._char = indent_char
def indent(self, level: int):
return self._char * level
class Type(IntEnum):
Text = 0
Node = 1
class Element(Line):
def __init__(self, name: str, indent_level: int = 0, indent_step: int = 2, indent_char: str = " "):
Line.__init__(self, indent_level, indent_step, indent_char)
assert isinstance(name, str)
self.name = name
self.children: list[Element] = []
self.attributes: dict[str, str] = {}
self.type: Type = Type.Text
@property
def attrs(self):
return " ".join(f'{key}="{value}"' for key, value in self.attributes.items())
def iter_lines(self, level: int):
if self.type is Type.Text:
yield self.indent(level) + self.name
return
if not self.children:
if self.attributes:
yield f"{self.indent(level)}<{self.name} {self.attrs} />"
else:
yield f"{self.indent(level)}<{self.name} />"
elif len(self.children) == 1 and self.children[0].type is Type.Text:
if self.attributes:
yield f"{self.indent(level)}<{self.name} {self.attrs}>{self.children[0].stringify()}</{self.name}>"
else:
yield f"{self.indent(level)}<{self.name}>{self.children[0].stringify()}</{self.name}>"
else:
if self.attributes:
yield f"{self.indent(level)}<{self.name} {self.attrs}>"
else:
yield f"{self.indent(level)}<{self.name}>"
yield from map(lambda child: child.stringify(level + self._step), self.children)
yield f"{self.indent(level)}</{self.name}>"
def stringify(self, base_level: int = 0):
return "\n".join(self.iter_lines(base_level))
__str__ = stringify
def __repr__(self):
return f"{self.__class__.__qualname__}({self.name!r})"
def append_child(self, other):
self.type = Type.Node
thing = self.__class__(other, self._level + 1, self._step)
self.children.append(thing)
return thing
__gt__ = __call__ = __add__ = append_child
def __radd__(self, other):
return other + self.stringify()
def __enter__(self):
self.type = Type.Node
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def __setitem__(self, key, value):
self.attributes[key] = value
class E(Element):
def __enter__(self):
Cursor.context.append(self)
return Element.__enter__(self)
def __exit__(self, exc_type, exc_val, exc_tb):
Cursor.context.pop()
class Cursor:
context: list[E] = [E("")]
@property
def current(self) -> E:
return self.context[-1]
def stringify(self):
return self.current.children[0].stringify()
__str__ = stringify
def __add__(self, other):
return self.current + other
def __call__(self, *args, **kwargs):
return self.current(*args, **kwargs)
def __gt__(self, other):
return self.current > other
if __name__ == '__main__':
_ = Cursor()
with _ > "html":
with _ > "head":
_("title") + "标题"
with _ > "body" as body:
with _("img") as img:
img["src"] = "图片地址"
img["alt"] = "图片标题"
with _("div"):
for i in range(10):
_("p") + f"第{i}句"
_("div")("div")("p")("ul")("li") + "还可以链式调用"
print(_)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment