组合模式 | 文档树结构
"""任务:实现组合模式表示文档层级结构场景:文档可能包含章节、段落、句子等嵌套结构"""class DocumentComponent(ABC):"""文档组件基类"""@abstractmethoddef get_text(self):pass@abstractmethoddef get_word_count(self):passclass Sentence(DocumentComponent):"""句子(叶子节点)"""def __init__(self, text):self.text = textdef get_text(self):return self.textdef get_word_count(self):return len(self.text.split())class Paragraph(DocumentComponent):"""段落(组合节点)"""def __init__(self):self.sentences = []def add(self, sentence: Sentence):self.sentences.append(sentence)def get_text(self):"""返回所有句子拼接"""return ' '.join(s.get_text() for s in self.sentences)def get_word_count(self):"""返回所有句子的总字数"""return sum(s.get_word_count() for s in self.sentences)class Chapter(DocumentComponent):"""章节(组合节点)"""def __init__(self, title):self.title = titleself.paragraphs = []def add(self, paragraph: Paragraph):self.paragraphs.append(paragraph)def get_text(self):para_texts = [p.get_text() for p in self.paragraphs]return f"# {self.title}\n\n" + '\n\n'.join(para_texts)def get_word_count(self):return sum(p.get_word_count() for p in self.paragraphs)# 测试chapter = Chapter("第一章:Python基础")# 第一段para1 = Paragraph()para1.add(Sentence("Python是一种编程语言。"))para1.add(Sentence("它简单易学。"))# 第二段para2 = Paragraph()para2.add(Sentence("Python广泛用于AI开发。"))chapter.add(para1)chapter.add(para2)# 测试text = chapter.get_text()word_count = chapter.get_word_count()print(text)print(f"\n总字数: {word_count}")assert "Python" in textassert word_count > 0print("\n✅ 组合模式测试通过")
一、场景
应用于有层级、树形结构、需要整体 / 单个都能用同一套方法操作的场景,整体和单个能用同样的方式使用,可以嵌套、递归统计 / 遍历 / 渲染,具体场景示例:
-
文档结构(章节 → 段落 → 句子)
-
文件目录(文件夹 → 子文件夹 → 文件)
-
菜单系统(主菜单 → 子菜单 → 菜单项)
-
组织架构(公司 → 部门 → 员工)
二、个人理解/最初卡在哪里
1、在下面语句加了 return
def add(self, sentence:Sentence):self.sentences.append(sentence)
组合模式里的 add 只需要 “添加子节点”,不需要返回任何东西。
list.append () 本身返回的是 None,如果加了return,调用 add 会得到 None,破坏组合模式。
2、中文字符统计
def get_word_count(self):return len(self.text.split())
split() 是按空格分词,只对英文有效,中文没有空格,所以统计出来字数是1句算1个词,所以运行后总字数为3。例如:
len("Python是一种编程语言。".split())
中文没有空格 → split 切不开 → 长度 = 1
英文:
len("Python is good".split()) → 3 正确
如果想正确统计中文字数(不含标点):
def get_word_count(self):# 只统计中文/英文/数字clean_text = re.sub(r'[^\w\u4e00-\u9fa5]', '', self.text)return len(clean_text)
-
\w:单词字符(在 Python 3 默认 Unicode 模式下,包括字母、数字、下划线,以及部分其他语言的字母数字,但不包括中文);
-
\u4e00–\u9fa5:Unicode 范围,对应常用汉字(CJK 统一表意文字,基本区,约 20992 个字符);
-
如果想严格只保留 ASCII 字母数字和下划线 + 汉字,可以设置 re.ASCII 标志:re.sub(r'[^\w\u4e00–\u9fa5]’, ”, text, flags=re.ASCII),这样 \w 就只匹配 [a-zA-Z0-9_]。
三、代码中的关键洞察
-
组合模式 = 树形结构 + 统一接口
-
add 方法只添加,不 return
-
单个对象和组合对象使用方式完全一样
四、关键代码/可复用片段
① 顶层抽象
class DocumentComponent(ABC):@abstractmethoddef get_text(self):pass@abstractmethoddef get_word_count(self):pass
② 最小单元:叶子(无 add)
class Sentence(DocumentComponent):def __init__(self, text: str):self.text = textdef get_text(self):return self.textdef get_word_count(self):return len(self.text.split())
③ 中间容器:只装 Sentence
class Paragraph(DocumentComponent):def __init__(self):self.sentences = []def add(self, sentence:Sentence):self.sentences.append(sentence)def get_text(self):return ' '.join(s.get_text() for s in self.sentences)def get_word_count(self):return sum(s.get_word_count() for s in self.sentences)
④ 顶层容器:只装 Paragraph
class Chapter(DocumentComponent):def __init__(self, title: str):self.title = titleself.paragraphs = []def add(self, paragraph: Paragraph):self.paragraphs.append(paragraph)def get_text(self):body = '\n\n'.join(p.get_text() for p in self.paragraphs)return f"# {self.title}\n\n{body}"def get_word_count(self):return sum(p.get_word_count() for p in self.paragraphs)
夜雨聆风