简介面向Python初学者及需要完成课程展示的学生这套资源包围绕面向对象编程核心概念展开包含14个文件其中13个Python源码脚本与1份PPT演示文稿压缩包大小约1.59MB。PPT系统梳理了类与对象、继承、封装、多态、魔术方法、访问控制修饰符及property属性控制等知识点配有图表和代码示例配套的.py源码覆盖各知识点对应的演示程序以及工厂模式、单例模式等设计模式实例便于边看边练。目前已有334人学习下载。借助这份资料读者既能快速搭建面向对象知识框架用于课堂展示或答辩也可以通过运行、修改源码深入体会__init__初始化、描述符、单继承与多继承等机制的实际用法还能结合源码分析设计模式的落地方式从而逐步提升面向对象编程能力适合初学者从概念到实践逐步过渡。1. 别拿 Python 写死幻灯片面向对象 Presentation 是批量化文档的必经之路每周五下午某公司在做销售周报 PPT手动把几十条数据贴进同一套版式再调字号、对齐、配色一干就是两小时。这种活儿第一次做是体力劳动第五次做就是浪费生命。用 Python 做 Presentation 自动化生成恰好能把这套排版过程变成代码流程尤其是用面向对象OOP的方式去组织而不是写一坨几百行的顺序脚本。python面向对象presentation——PPT含源码.rar 这个标题指的就是这么一套东西用 python-pptx 对象模型去操作演示文稿再通过类封装复用版式和内容逻辑最终输出多页成品 PPT。适合谁做这套方案需要批量生成周报、培训教材、宣讲 PPT、或者要把数据库里的报表转成演示文档的开发者都能从中拿到收益。新手能借此把 python-pptx 的基本 API 跑通熟手能借鉴里面的继承结构和避坑思路省掉自己在母版和占位符上摸黑的时间。这篇文章就是按这个落地路径讲的先看清对象模型然后写最小可运行代码再升级到可维护的 OOP 架构最后把常见的翻车点挨个补上。2. 先看透 python-pptx 的对象图Presentation、Slide 与 Shape 是一次生成逻辑的本体很多人拿到标题里的“面向对象”就发怵以为要顺带学设计模式。其实在 PPT 自动化这个场景里面向对象不是一种理论装饰而是 python-pptx 这个库底层的工作方式。你不理解这层对象关系后面写出来的代码就是一团乱麻。2.1 为什么非要用“面向对象”来写 PPT 脚本常见的自动化做法是顺序脚本打开模板找标题填文本加图片保存。第一次用能跑通第二次换了内容就要复制粘贴改一大片。问题出在“找标题”“填文本”这些动作被散落在各处没有一个稳定的边界。而面向对象做的事是把“一页幻灯片长什么样”抽象成类把“内容从哪来”和“内容怎么放”分开。我一般会这么理解把每一页 PPT 看作一个对象页面里的标题、正文、图片是它的属性排版动作是它的方法。换一批数据只需要重新 new 这个类不需要改排版逻辑。这个思路跟 python-pptx 本身的 API 结构天然匹配硬拿 for 循环往里塞反而别扭。某开发者接手过一份旧的生成脚本两千行全靠列表推导式写文本定位一改版式就崩后来重构成类结构改动量直接缩到原来的四分之一。2.2 python-pptx 的对象树Presentation 是根Slide/Shape 是节点任何基于 python-pptx 的程序解剖开都是一棵对象树。顶层是 Presentation它代表整个 pptx 文件包含全局设置、尺寸、主题和幻灯片列表。向下走一层是 Slide对应一页演示内容再向下是 Shape包括文本框、图片、表格、图表这些具体元素。每一层都有属性比如 Presentation 有 slide_widthSlide 有 slide_layoutShape 有 left/top/width/height。这套模型跟传统的图形界面编程很接近难点不在“找到对象”而在“搞清楚层级关系”。常见的错误是直接拿 Presentation 去 add_textbox实际上文本必须挂在 Slide 下面或者以为设置 Slide 的 background 就能改背景实际上要操作 Shape 树里的最底层元素。把这些关系在脑子里画成树写出来的代码就顺了。python-pptx 源码包里的 doc 目录有示例但抽象能力弱的人读起来吃力建议直接拿一个小 pptx 文件跑 dir() 逐个看属性这个习惯能省掉后面 70% 的排错时间。3. 一套能跑通的最小封装从空白 PPT 到四页内容页的落地代码原理说完了动手。环境准备这一步必须做对因为 python-pptx 的安装虽然简单但虚拟环境和依赖锁定稍有偏差后面导入就报错属于那种“明明照着敲却跑不起来”的经典开局。3.1 环境准备安装 python-pptx 这一步有玄机我通常会先建一个干净的虚拟环境而不是直接塞进全局 Python 里。原因很实际机器上可能同时装着办公自动化相关的其他库版本互相踩踏时会出一些很难查的错误。python -m venv ppt_env # 激活虚拟环境Windows 下执行 ppt_env\Scripts\activateLinux/macOS 执行 source ppt_env/bin/activate pip install python-pptx这里的参数没有花哨的版本号因为 python-pptx 的 API 在近几个大版本里基本稳定按官方最新版装即可。如果将来要把脚本交给别人复现建议把 python-pptx 写进 requirements.txt 时检查一下实际版本避免换台电脑被奇怪的兼容性问题卡住。装完以后验证导入python -c from pptx import Presentation; print(ok)能打印 ok说明环境这关过了。这一步看起来废话但很多“代码没问题就是跑不了”的案例最后都死在环境上不值得为这个浪费时间。3.2 最小命令三行代码建出第一张幻灯片接下来目标是生成一个最简单的 pptx 文件一页空白幻灯片带一个标题框。这里的关键是从模板文件加载 Presentation而不是裸创建一个空白对象因为模板里才有母版和版式。python-pptx 默认自带一个模板内部叫 Default Template里面预置了常用的版式比如“标题和内容”“仅标题”“空白”。from pptx import Presentation from pptx.util import Inches prs Presentation() # 设置页面尺寸为标准 16:9宽 13.333 英寸高 7.5 英寸 prs.slide_width Inches(13.333) prs.slide_height Inches(7.5) # 选用“仅标题”版式即 slide_layouts[5] 对应的布局 slide_layout prs.slide_layouts[5] slide prs.slides.add_slide(slide_layout) # 给标题占位符赋值 slide.shapes.title.text 第一页你好python-pptx prs.save(first_slide.pptx)这段代码的核心逻辑是三步创建 Presentation选择版式并添加幻灯片再往占位符里写文字。参数上要说明的是 Inchespython-pptx 内部使用英制单位Inches 是长度包装器也可以用 Pt 或 Cm但混用单位时容易算错尺寸建议全篇统一。slide_layouts[5] 在不同模板里含义可能不同默认模板里它是“仅标题”如果你用的模板是 A公司的内部母版索引要自己打印 slide_layouts 逐个确认不要靠猜。运行完这段同目录下会多出一个 first_slide.pptx。用 WPS 或 Office 打开能看到一页干净的 16:9 幻灯片只有一个顶部标题。这就是整个自动化流程的最小闭环后面的复杂度都是在这个骨架上长出来的。3.3 从单个幻灯片到多页循环数据结构与模板分离最小案例跑通后接着就是要解决真实的问题很多页内容但版式一样。这时候如果还是逐页写代码就失去了自动化的意义。常见做法是把内容从代码里剥离出来用列表或字典存数据然后循环套版式。from pptx import Presentation from pptx.util import Inches prs Presentation() prs.slide_width Inches(13.333) prs.slide_height Inches(7.5) # 内容数据与排版逻辑分离后续换数据只改这里 content_list [ {title: 一季度销售概况, points: [营收同比 14%, 华东区贡献 47%, 新客户占比 22%]}, {title: 二季度目标, points: [总体营收破 3000 万, 重点搞定华南区, 产品线 B 尽快转正]}, {title: 风险与对策, points: [供应链交付不稳定, 竞品降价冲击中端, 对策增加备选供应商]}, ] for item in content_list: slide_layout prs.slide_layouts[1] # “标题和内容”版式 slide prs.slides.add_slide(slide_layout) slide.shapes.title.text item[title] # 第二个占位符通常是内容主体此处写入多行正文 body slide.placeholders[1] body.text \n.join(item[points]) prs.save(multi_page.pptx)这里有个参数细节slide.placeholders[1]拿到的是页面上的第二个占位符也就是正文框。它跟 shapes.title 一样都是 Shape 对象但类型不同——前者是 Placeholder后者在大部分版式里也是 Placeholder。用\n.join(...)是给每个 body 对象一次性赋多段文本效率比逐行添加段落高而且代码短。如果还想控制每行字体的差异就得走 TextFrame 的 paragraph 和 run 层级这在后文避坑部分会专门提。到这一步你已经能批量生成基础 PPT 了。但标题里写了“面向对象”如果你只停留在循环赋值那还停留在“用 Python 代替手工打字”的阶段。下一章把代码重构成真正的类结构这才是“含源码”的价值所在。4. 让源码配得上“面向对象”继承基类与组合排版的双层架构批量生成脚本一旦涉及多套版式、多种排版逻辑靠复制for循环就会失控。这一章给出一套能撑住真实项目的 OOP 骨架核心思想是两层基类继承管“同一类页面的公共排版”组合模式管“不同页面在演示文稿里的组装顺序”。4.1 先抽象一个 BaseSlide 基类把公共逻辑收进来观察所有页面的共性都要接收 Presentation 和内容数据都要为自己选版式都要把自己加到 prs.slides 里。把这些公共行为下沉到基类子类只关心自己的个性部分。下面给出一段可以直接照抄的骨架。from pptx import Presentation from pptx.util import Inches from abc import ABC, abstractmethod class BaseSlide(ABC): 所有页面的基类负责选版式、添加幻灯片、保存结果 def __init__(self, prs: Presentation, layout_idx: int, title: str): self.prs prs self.layout_idx layout_idx self.title title self.slide None def add_slide(self): 在 prs 上新增一页并赋标题 self.slide self.prs.slides.add_slide(self.prs.slide_layouts[self.layout_idx]) self.slide.shapes.title.text self.title return self.slide abstractmethod def render(self, data): 子类实现把数据填到当前 self.slide 上 passBaseSlide 把“创建幻灯片 设置标题”这两步动作收进 add_slide。子类重写 render里面只写跟自身版式强相关的操作。这样公共逻辑改一处全线生效。比如以后所有页面加个页码角标改这里就够。layout_idx 作为参数传入而不是写死在基类里是因为不同子类会用到不同的版式基类不该替子类做选择。4.2 组合设计模板版式与内容分离给未来留后悔药有了基类接着定义几种具体页面类型。封面页、目录页、内容页、结尾页它们各自实现 render 方法排版代码只出现在对应子类里。下面看一个封面页和内容页的实现。class CoverSlide(BaseSlide): def __init__(self, prs: Presentation): super().__init__(prs, layout_idx0, title) # 默认模板的 0 号版式是“标题幻灯片” def render(self, data): self.add_slide() # 封面通常要副标题位置在 shapes[1] self.slide.placeholders[1].text data[subtitle] class ContentSlide(BaseSlide): def __init__(self, prs: Presentation): super().__init__(prs, layout_idx1, title) # 1 号版式是“标题和内容” def render(self, data): self.add_slide() self.slide.shapes.title.text data[title] body self.slide.placeholders[1] body.text \n.join(data[points])这两个子类没有任何循环排版逻辑render 就是纯声明式地描述“这页长什么样”。在组装阶段只需要按顺序创建对象、调用 render就完成了整份 PPTprs Presentation() prs.slide_width Inches(13.333) prs.slide_height Inches(7.5) # 组装顺序在这里确定改动顺序无需触碰子类代码 CoverSlide(prs).render({subtitle: 2025 年度经营复盘}) ContentSlide(prs).render({title: 关键指标, points: [营收 1.2 亿, 毛利 34%, 复购率 51%]}) prs.save(oop_ppt.pptx)这里的组合关系体现得很清晰BaseSlide 是“继承”层面的通用能力CoverSlide 和 ContentSlide 是其特化而组装时用的是“组合”即 Presentation 持有多个 BaseSlide 子类实例按顺序摆放。这种架构下以后加一种新的信息图表页只需要新增一个子类不需要去改动页面外的任何代码这就是留下后悔药的做法。代码里还有一处值得关注self.slide.shapes.title.text在子类里重复写了。理论上可以继续往基类下沉但实际中封面页的标题可能希望空着然后自行排版所以保留在子类里更具弹性。设计的原则不是“代码越少越好”而是“变化隔离得越干净越好”。5. 生成 PPT 的常年翻车点占位符、母版、中文字体与缩放的血泪排查OOP 架构能解决代码组织问题但解决不了 Office 渲染层面那堆“玄学”。下面这几条是我和周围开发者反复踩过的坑每一条的教训都是拿真实翻车经历换来的按“现象→原因→解决”写供你排查时直接对照。5.1 母版占位符没被填充导出变成黑块或空白现象代码跑完没有任何报错生成的 pptx 打开以后整页是一片空白或者只有默认的提示文案自己写入的文本全不见了。原因部分模板的版式里占位符并不在 shapes 树上或者存在“空占位符”的概念。这时候用slide.placeholders[0]拿到的可能是 None你没做判空就赋值代码静默跳过最终页面用的还是母版里那张“空提示图”。解决在渲染逻辑里先判断 placeholders 是否为空写成防御式代码if slide.placeholders: title_placeholder slide.shapes.title if title_placeholder is not None: title_placeholder.text data[title]5.2 图片插入后被压缩变形不是代码问题是比例没锁现象用add_picture插入了一张 1500x800 的矩形图设置widthInches(5)高度没写结果图片被横向压扁。原因python-pptx 的 add_picture 支持只指定 width 或 height 其一另一个方向会自动等比缩放。但有些人急着写代码时把两个值都写死了比如 width 和 height 分别按模板格子大小设置忘记保持原图宽高比。解决要么只传 width 让库自动算高度要么先用 PIL 或图片元数据读取原始宽高算出等比高度再传入。常见标准from PIL import Image img_path chart.png img Image.open(img_path) ratio img.height / img.width pic_width Inches(5) pic_height Inches(5 * ratio) slide.shapes.add_picture(img_path, Inches(1), Inches(2), widthpic_width, heightpic_height)5.3 中文乱码或替换成宋体系统的字体库是隐形黑匣子现象自己的开发机上生成的 PPT 中文一切正常换一台只有英文环境的机器打开中文全部变成宋体或者方框。原因python-pptx 设置字体名只是写入 XML最终渲染由打开 PPT 的机器决定。如果目标机器上没有设置过对应中文字体Office 就会自动回退到系统默认字体。解决一种思路是在代码里显式给每个 run 设置东亚字体但 python-pptx 默认不暴露该入口更稳妥的做法是在模板文件里设置母版默认字体或者在使用端环境补装常用中文字体。切记不要指望 python-pptx 能自动把字体嵌进去它做不到。5.4 文字自动缩小的“玄学”设置 word_wrap 和 auto_size 要配套现象文本框里的文字一长打开 PPT 发现字号被 Office 自动缩小了看着极不整齐。代码里明明没写 auto_size它却自动变了。原因Office 的文本框默认启用了“溢出时缩字”功能对应的 XML 属性是auto_size在 python-pptx 中对应text_frame.auto_size。如果你用的模板文件本身设置了该属性新建页面会继承导致代码里不设置也会出现缩放。解决显式关闭自动缩放同时开启自动换行from pptx.enum.text import MSO_AUTO_SIZE, MSO_ANCHOR tf slide.placeholders[1].text_frame tf.auto_size MSO_AUTO_SIZE.NONE tf.word_wrap True设置之后字长时会在框内换行超过边界就直接溢出不会再被 Office 自作主张缩小。5.5 背景图挡不住文字层级顺序与重新排序现象用模板自带母版背景图再往页面上添加文本框内容生成后文字显示在背景下面直接看不见。原因Shape 的默认层级是“后添加的在上层”。但部分模板的母版或版式里嵌着底层形状如果添加文本时用的占位符失效Python 拿不到插入点就会变成一个游离的新形状排到了背景之后。解决控制 shape 层级需要操作 XML 树Python 侧没有公开 API。常见做法是遍历 shapes把目标形状移动到最顶层from pptx.oxml.ns import qn from pptx.enum.shapes import MSO_SHAPE_TYPE # 把指定 shape 移到顶层 def move_shape_to_top(slide, shape): tree slide.shapes._spTree sp shape._element sp.getparent().remove(sp) tree.append(sp)如果发现背景盖文字直接调用这个函数把文本形状挪到顶层即可。这招也算是在 python-pptx 边缘功能里翻出来的后悔药。6. 最后一手把模板文件当配置传入让同一套 OOP 代码产出不同风格如果你已经跑通了前面章节的代码并且踩过 5.15.5 里的某些坑再往前一步就是让这套 OOP 架构真正具备产品价值。我常用的最后一招是把模板文件路径也作为配置传入。这样你的源码不需要改一行排版代码就能通过切换模板输出完全不同的视觉风格。# render_with_config.py from pptx import Presentation from pptx.util import Inches def build_deck(template_path: str, content_pages: list, output_path: str): prs Presentation(template_path) # 关键从模板加载而非默认空白 prs.slide_width Inches(13.333) prs.slide_height Inches(7.5) for page in content_pages: # 这里的 page 是 dict包含 layout_idx/title/points if page[type] content: slide prs.slides.add_slide(prs.slide_layouts[page[layout_idx]]) slide.shapes.title.text page[title] slide.placeholders[1].text \n.join(page[points]) prs.save(output_path) # 使用示例 build_deck(template_companyA.pptx, content_list, report_A.pptx)这套写法的收益很直接你只需要维护一套 Python 源码多套模板文件放在同一个目录下换风格根本不需要改代码。把模板路径、输出路径、内容数据三者放进配置文件整个脚本就变成一个小型批处理工具。最后说一个我自己的使用习惯无论是 BaseSlide 还是 5.5 节的移动层级函数我都坚持把常用功能收进一个单独的工具模块而不是让它在每个页面类里重复出现。因为模板越换越多样某些母版会很顽固地不按文档里的默认行为走这时候有一个集中的“应急工具箱”比临时翻 API 救命要快得多。我也因为偷懒跳过 5.3 的字体检查吃过亏那次生成的两百页 PPT 在客户机器上全部变回宋体整整一个下午都在远程救火后来再也没省过模板字体校验这一步。这套面向对象 Presentation 方案的边界也很明确它适合批量、重复、结构化内容的生成不适合对视觉美感有极高要求的定制设计。真到了那个程度还是要交给人工排版。但在 80% 的日常汇报场景里能用代码解决的事就不要再用手工浪费时间了。希望这篇落地笔记帮到你也希望你上手时少踩几个我踩过的坑。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?