news 2026/3/7 15:05:34

Python 访问者模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python 访问者模式

Python 中的访问者模式(Visitor Pattern)

访问者模式是一种行为型设计模式,其核心目的是:
将算法(操作)与对象结构分离,让你在不改变对象结构的前提下,为该结构中的元素添加新的操作。

形象比喻:就像一个动物园(对象结构)里有很多动物(元素),来了不同的“访客”(访问者)——摄影师会拍照、饲养员会喂食、兽医会检查健康。动物本身不需要改变,就能支持不同的新操作。

为什么需要访问者模式?

当你有一个稳定的对象结构(例如 AST 抽象语法树、图形元素树、文件系统),但需要频繁添加新操作时:

  • 如果用继承:在每个类中添加新方法 → 违反开闭原则
  • 如果用条件判断:分散在各个类中 → 难以维护

访问者模式通过双分派(Double Dispatch)解决这个问题:第一次分派决定访问者,第二次分派决定具体元素。

典型应用场景
  • 编译器:对 AST(抽象语法树)进行不同操作(类型检查、代码生成、优化、打印)
  • 文档转换:将结构化文档转为 HTML、PDF、Markdown
  • 图形渲染:对图形元素树执行绘制、计算面积、序列化等操作
  • 报表统计:在组织架构树上统计人数、薪资等
  • XML/JSON 处理:遍历 DOM 树执行不同操作
Python 实现示例:图形元素访问者

我们实现一个简单的图形编辑器,支持圆形和矩形,访问者包括:绘制、计算面积、导出 XML。

fromabcimportABC,abstractmethodfromtypingimportListimportxml.etree.ElementTreeasET# === 元素接口(Element)===classShape(ABC):@abstractmethoddefaccept(self,visitor):pass# 具体元素1:圆形classCircle(Shape):def__init__(self,x:float,y:float,radius:float):self.x,self.y,self.radius=x,y,radiusdefaccept(self,visitor):returnvisitor.visit_circle(self)# 具体元素2:矩形classRectangle(Shape):def__init__(self,x:float,y:float,width:float,height:float):self.x,self.y,self.width,self.height=x,y,heightdefaccept(self,visitor):returnvisitor.visit_rectangle(self)# === 访问者接口(Visitor)===classShapeVisitor(ABC):@abstractmethoddefvisit_circle(self,circle:Circle):pass@abstractmethoddefvisit_rectangle(self,rectangle:Rectangle):pass# 具体访问者1:绘制访问者classDrawVisitor(ShapeVisitor):defvisit_circle(self,circle:Circle):print(f"绘制圆形:中心({circle.x},{circle.y}), 半径{circle.radius}")defvisit_rectangle(self,rectangle:Rectangle):print(f"绘制矩形:左上角({rectangle.x},{rectangle.y}), "f"宽{rectangle.width}, 高{rectangle.height}")# 具体访问者2:面积计算访问者classAreaVisitor(ShapeVisitor):def__init__(self):self.total_area=0.0defvisit_circle(self,circle:Circle):importmath area=math.pi*circle.radius**2self.total_area+=areaprint(f"圆形面积:{area:.2f}")defvisit_rectangle(self,rectangle:Rectangle):area=rectangle.width*rectangle.height self.total_area+=areaprint(f"矩形面积:{area:.2f}")# 具体访问者3:XML 导出访问者classXMLExportVisitor(ShapeVisitor):def__init__(self):self.root=ET.Element("shapes")defvisit_circle(self,circle:Circle):elem=ET.SubElement(self.root,"circle")elem.set("x",str(circle.x))elem.set("y",str(circle.y))elem.set("radius",str(circle.radius))defvisit_rectangle(self,rectangle:Rectangle):elem=ET.SubElement(self.root,"rectangle")elem.set("x",str(rectangle.x))elem.set("y",str(rectangle.y))elem.set("width",str(rectangle.width))elem.set("height",str(rectangle.height))defget_xml(self)->str:returnET.tostring(self.root,encoding='unicode')# 对象结构:画布(可以包含多个图形)classCanvas:def__init__(self):self.shapes:List[Shape]=[]defadd(self,shape:Shape):self.shapes.append(shape)defaccept(self,visitor:ShapeVisitor):forshapeinself.shapes:shape.accept(visitor)# 客户端使用if__name__=="__main__":canvas=Canvas()canvas.add(Circle(10,20,5))canvas.add(Rectangle(30,40,15,10))canvas.add(Circle(50,50,8))print("=== 绘制所有图形 ===")draw_visitor=DrawVisitor()canvas.accept(draw_visitor)print("\n=== 计算总面积 ===")area_visitor=AreaVisitor()canvas.accept(area_visitor)print(f"总面积:{area_visitor.total_area:.2f}")print("\n=== 导出为 XML ===")xml_visitor=XMLExportVisitor()canvas.accept(xml_visitor)print(xml_visitor.get_xml())

输出

=== 绘制所有图形 === 绘制圆形:中心(10, 20), 半径 5 绘制矩形:左上角(30, 40), 宽 15, 高 10 绘制圆形:中心(50, 50), 半径 8 === 计算总面积 === 圆形面积: 78.54 矩形面积: 150.00 圆形面积: 201.06 总面积: 429.60 === 导出为 XML === <shapes><circle x="10" y="20" radius="5" /><rectangle x="30" y="40" width="15" height="10" /><circle x="50" y="50" radius="8" /></shapes>
访问者模式结构总结
角色说明
Visitor抽象访问者接口(ShapeVisitor)
ConcreteVisitor具体访问者(DrawVisitor、AreaVisitor 等)
Element元素接口(Shape),定义 accept 方法
ConcreteElement具体元素(Circle、Rectangle)
Object Structure对象结构(Canvas),管理元素集合
访问者模式 vs 其他模式对比
模式目的扩展方向典型场景
访问者在稳定结构上添加新操作添加新操作AST 处理、文档转换
组合构建树形结构添加新元素GUI 树、文件系统
策略替换算法替换行为支付、排序
命令封装请求添加新命令撤销、宏
Python 中的实用建议
  • 访问者模式在 Python 中使用较少,因为 Python 是动态语言,很多场景可以用:
    • 函数作为访问者(传入不同函数)
    • getattr(element, operation_name)()动态调用
    • 多分派库(如functools.singledispatchmultipledispatch

更 Pythonic 的替代方式

fromfunctoolsimportsingledispatch@singledispatchdefprocess_shape(shape):raiseNotImplementedError(f"Unsupported shape:{type(shape)}")@process_shape.registerdef_(shape:Circle):print(f"处理圆形: 半径{shape.radius}")@process_shape.registerdef_(shape:Rectangle):print(f"处理矩形: 宽高{shape.width}x{shape.height}")# 使用forshapeincanvas.shapes:process_shape(shape)
注意事项
  • 访问者模式违反了“依赖倒置原则”(高层依赖抽象),因为访问者需要知道所有具体元素类型
  • 添加新元素类型时,需要修改所有访问者(违反开闭原则)
  • 适合元素结构稳定、操作频繁变化的场景
  • 如果元素经常变化,考虑用组合 + 访问者双向结合
总结

访问者模式是处理稳定数据结构 + 多变操作的经典解决方案,在编译器、解释器、序列化等系统中非常常见。
但在 Python 中,由于语言的动态性,通常优先考虑更简单的方案(如 singledispatch、函数式编程)。

如果你想看更实际的例子(如 AST 遍历、HTML 渲染器、报表统计访问者),或者如何结合组合模式构建复杂结构,欢迎继续问!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/5 19:00:15

IINA终极指南:macOS视频播放器的10个高效使用方案

IINA终极指南&#xff1a;macOS视频播放器的10个高效使用方案 【免费下载链接】iina 项目地址: https://gitcode.com/gh_mirrors/iin/iina 您是否曾经为在macOS上找不到理想的视频播放器而烦恼&#xff1f;当您需要播放多种格式的视频文件、管理复杂的字幕需求&#xf…

作者头像 李华
网站建设 2026/3/6 5:43:26

Open-AutoGLM悄然崛起:与ChatGLM的4个关键区别你必须掌握

第一章&#xff1a;Open-AutoGLM与ChatGLM的演进背景近年来&#xff0c;随着大规模语言模型技术的快速发展&#xff0c;自然语言处理在多个领域实现了突破性进展。其中&#xff0c;ChatGLM系列模型作为智谱AI推出的高性能双语对话模型&#xff0c;凭借其基于GLM&#xff08;Gen…

作者头像 李华
网站建设 2026/3/4 12:46:20

AutoAWQ深度解析:大模型量化加速的完整解决方案

AutoAWQ深度解析&#xff1a;大模型量化加速的完整解决方案 【免费下载链接】AutoAWQ AutoAWQ implements the AWQ algorithm for 4-bit quantization with a 2x speedup during inference. 项目地址: https://gitcode.com/gh_mirrors/au/AutoAWQ 还在为大型语言模型的高…

作者头像 李华
网站建设 2026/3/5 14:37:07

为什么越来越多开发者选择Dify进行Agent开发?

为什么越来越多开发者选择 Dify 进行 Agent 开发&#xff1f; 在企业智能化浪潮席卷各行各业的今天&#xff0c;AI 不再只是实验室里的前沿技术&#xff0c;而是真正走进了客服窗口、内容工厂和内部知识系统。尤其是以大语言模型&#xff08;LLM&#xff09;为核心的 AI Agent …

作者头像 李华
网站建设 2026/2/18 8:09:12

专业级iOS设备越狱:palera1n工具深度解析与实战指南

在iOS生态系统的安全研究领域&#xff0c;设备越狱始终占据着重要地位。本文将为技术爱好者和安全研究人员深入剖析palera1n越狱工具的技术架构、操作原理及实战应用&#xff0c;帮助您全面掌握这一专业级越狱解决方案。 【免费下载链接】palera1n Jailbreak for arm64 devices…

作者头像 李华
网站建设 2026/3/4 4:01:19

解锁科研新维度:书匠策AI如何重塑期刊论文写作生态

在科研领域&#xff0c;期刊论文的撰写与发表始终是学者们攀登学术高峰的核心挑战。从浩如烟海的文献中定位研究方向&#xff0c;到构建逻辑严密的论证体系&#xff0c;再到打磨符合学术规范的文本表达&#xff0c;每一步都考验着研究者的智慧与耐力。而今&#xff0c;一款名为…

作者头像 李华