news 2026/5/2 23:24:20

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/5/2 6:32:42

错过等一年!Open-AutoGLM 2.0正式上线GitHub,这些功能你必须掌握

第一章&#xff1a;错过等一年&#xff01;Open-AutoGLM 2.0正式发布Open-AutoGLM 2.0 正式上线&#xff0c;标志着自动化大模型应用开发迈入全新阶段。该版本在性能、易用性和扩展性方面实现全面升级&#xff0c;专为开发者与企业用户打造高效、灵活的AI解决方案构建平台。核心…

作者头像 李华
网站建设 2026/5/1 5:23:36

破解“写作围城”:当期刊投稿遇上行家级AI协作者

文献迷雾中不再焦虑&#xff0c;智能工具重构写作全流程的效率与质量深夜的实验室&#xff0c;屏幕上摊着十几个窗口——文献PDF、草稿文档、数据表格和格式混乱的参考文献列表&#xff0c;学者们正试图从数字碎片中拼凑论文的完整形态&#xff0c;这种场景几乎成为科研通病。传…

作者头像 李华
网站建设 2026/5/1 5:24:15

12、GAN技术:从渐进式生成到半监督学习的突破

GAN技术:从渐进式生成到半监督学习的突破 1. 渐进式生成对抗网络(Progressive GAN)的实际应用 1.1 医学影像合成的卓越成果 在医学领域,研究人员利用大量的医学乳腺X光片数据集,借助渐进式生成对抗网络(Progressive GAN,简称PGGAN)技术,成功生成了分辨率高达1280 …

作者头像 李华
网站建设 2026/5/1 5:21:37

17、CycleGAN与对抗样本:原理、训练与应用

CycleGAN与对抗样本:原理、训练与应用 1. CycleGAN概述 CycleGAN是一种强大的图像到图像转换模型,它能够在无需配对图像数据的情况下,实现不同领域之间的图像转换,例如将苹果转换为橙子,反之亦然。下面我们将详细介绍CycleGAN的构建、训练和应用。 1.1 构建生成器 生成…

作者头像 李华
网站建设 2026/5/2 5:39:12

18、对抗样本:从原理到防御的全面解析

对抗样本:从原理到防御的全面解析 1. 训练数据的挑战 在处理图像数据时,即使是同一类别的图像,当拍摄角度稍有变化,它们之间的差异也可能很大。以一个包含100,000个300300的RGB图像的训练集为例,我们需要处理270,000个维度的数据。当考虑所有可能的图像(而非实际观察到…

作者头像 李华