【Python 进阶】抽象基类(Abstract Base Class)

news2025/12/14 21:53:40

abc 模块和抽象基类(Abstract Base Class)的概念:

本文的示例代码来源于 break-a-scene.

  • abc 是 Python 标准库中的一个模块,全称是 “Abstract Base Classes”(抽象基类)。
  • 它提供了一种定义抽象类和抽象方法的机制。
  • 在以下代码中,AttentionControl 类继承自 abc.ABC,表示这是一个抽象基类。

让我详细解释一下关键点:

  1. 抽象基类的作用:
  • 定义了一个接口规范
  • 强制子类必须实现某些方法
  • 不能直接实例化,必须通过子类来实现
  1. 在代码中的具体应用:
class AttentionControl(abc.ABC):
    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError
  • @abc.abstractmethod 装饰器标记 forward 方法为抽象方法
  • 任何继承 AttentionControl 的子类必须实现 forward 方法
  • 如果子类没有实现这个方法,在实例化时会报错
  1. 实际使用示例:
# 错误示例 - 会报错
controller = AttentionControl()  # 不能直接实例化抽象类

# 正确示例 - 需要实现抽象方法
class MyController(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        # 实现具体的逻辑
        return attn

这种设计模式的好处是:

  1. 确保所有子类都实现了必要的方法
  2. 提供了一个统一的接口规范
  3. 方便代码的维护和扩展

在这个项目中,AttentionControl 作为抽象基类,定义了注意力控制的基本接口,而具体的实现(如 AttentionStoreAttentionReplace 等)都是它的子类,各自实现了不同的注意力控制策略。

让我解释一下这两种实例化的区别:

  1. controller = AttentionControl() 会报错,因为:
  • AttentionControl 是一个抽象基类(Abstract Base Class)
  • 它包含抽象方法 forward(被 @abc.abstractmethod 装饰)
  • 抽象类不能直接实例化,必须通过子类来实现
  1. self.controller = AttentionStore() 是合法的,因为:
  • AttentionStoreAttentionControl 的子类
  • 它实现了所有必需的抽象方法(包括 forward 方法)
  • 子类可以正常实例化

让我们看看代码中的具体实现:

# 父类(抽象基类)
class AttentionControl(abc.ABC):
    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError

# 子类(具体实现)
class AttentionStore(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        key = f"{place_in_unet}_{'cross' if is_cross else 'self'}"
        if attn.shape[1] <= 32**2:
            self.step_store[key].append(attn)
        return attn

这就像是一个"合同":

  1. AttentionControl 定义了一个"合同",要求所有子类必须实现 forward 方法
  2. AttentionStore 签署了这个"合同",通过实现 forward 方法来满足要求
  3. 因此 AttentionStore 可以实例化,而 AttentionControl 不能

这种设计模式的好处是:

  1. 确保所有注意力控制器都实现了必要的方法
  2. 提供了统一的接口规范
  3. 方便代码的维护和扩展
  4. 可以在运行时检查类型是否正确

所以简单来说:

  • AttentionControl() 会报错,因为它是一个"合同模板"
  • AttentionStore() 可以正常工作,因为它是一个"已签署的合同"

继承 abc.ABC 的好处

让我解释一下为什么 AttentionControl 要继承 abc.ABC

  1. 不是绝对必要的,但是继承 abc.ABC 有几个重要好处:
# 方式1:继承 abc.ABC
class AttentionControl(abc.ABC):
    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError

# 方式2:不继承 abc.ABC
class AttentionControl:
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError
  1. 继承 abc.ABC 的主要好处:

a) 强制抽象方法实现:

# 如果不继承 abc.ABC,子类可以不实现 forward 方法
class BadController(AttentionControl):
    pass  # 这不会报错

# 如果继承 abc.ABC,子类必须实现 forward 方法
class GoodController(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        return attn  # 必须实现

b) 提供类型检查:

# 继承 abc.ABC 后,可以用于类型检查
def process_controller(controller: AttentionControl):
    # 确保 controller 实现了所有必要的方法
    pass

c) 更好的代码文档:

  • 明确表示这是一个抽象基类
  • 表明这个类是用来被继承的
  • 表明这个类定义了接口规范
  1. 实际应用中的区别:
# 不继承 abc.ABC 的问题
class AttentionControl:
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError

class BadController(AttentionControl):
    pass  # 可以实例化,但会报错

controller = BadController()  # 可以创建实例
controller.forward()  # 运行时才会报错

# 继承 abc.ABC 的好处
class AttentionControl(abc.ABC):
    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError

class BadController(AttentionControl):
    pass  # 直接报错,不能实例化

controller = BadController()  # 直接报错,不能创建实例
  1. 为什么选择继承 abc.ABC

a) 更早的错误检测:

  • 在类定义时就能发现错误
  • 而不是在运行时才发现

b) 更清晰的接口定义:

  • 明确表示这是一个抽象基类
  • 明确哪些方法必须被实现

c) 更好的代码维护:

  • 更容易理解代码结构
  • 更容易发现潜在问题

d) 更好的类型提示:

  • IDE 可以提供更好的代码补全
  • 静态类型检查工具可以工作

所以,虽然继承 abc.ABC 不是绝对必要的,但它提供了:

  1. 更好的错误检测
  2. 更清晰的接口定义
  3. 更好的代码维护性
  4. 更好的类型提示

这些好处使得代码更加健壮和可维护,特别是在大型项目中。

继承的好处

为什么 AttentionStore 要继承 AttentionControl,以及这样做的好处:

  1. 必须这么做吗?
  • 不是必须的,但这是一个很好的设计模式
  • 如果不继承,也可以直接实现所有功能
  • 但继承 AttentionControl 提供了更好的代码结构和可维护性
  1. 这样做的好处:
class AttentionControl(abc.ABC):
    def __call__(self, attn, is_cross: bool, place_in_unet: str):
        if self.cur_att_layer >= self.num_uncond_att_layers:
            h = attn.shape[0]
            attn[h // 2 :] = self.forward(attn[h // 2 :], is_cross, place_in_unet)
        self.cur_att_layer += 1
        if self.cur_att_layer == self.num_att_layers + self.num_uncond_att_layers:
            self.cur_att_layer = 0
            self.cur_step += 1
            self.between_steps()
        return attn

a) 代码复用:

  • AttentionControl 提供了通用的注意力控制逻辑
  • 子类只需要实现特定的 forward 方法
  • 避免了重复编写相同的代码

b) 统一接口:

  • 所有注意力控制器都遵循相同的接口
  • 确保所有控制器都有必要的方法
  • 方便其他代码使用这些控制器

c) 扩展性:

  • 可以轻松添加新的注意力控制器
  • 只需要继承 AttentionControl 并实现 forward 方法
  • 不需要重写所有逻辑

d) 维护性:

  • 如果需要修改基础逻辑,只需要修改 AttentionControl
  • 所有子类都会自动获得更新
  • 减少了代码重复和潜在的错误
  1. 实际应用示例:
# 基础控制器
class AttentionControl(abc.ABC):
    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError

# 存储控制器
class AttentionStore(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        # 只实现存储逻辑
        key = f"{place_in_unet}_{'cross' if is_cross else 'self'}"
        if attn.shape[1] <= 32**2:
            self.step_store[key].append(attn)
        return attn

# 替换控制器
class AttentionReplace(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        # 实现替换逻辑
        return torch.einsum("hpw,bwn->bhpn", attn_base, self.mapper)

这种设计模式的好处是:

  1. 代码更加模块化
  2. 更容易维护和扩展
  3. 减少了代码重复
  4. 提供了统一的接口
  5. 方便添加新的功能

所以,虽然不是必须的,但继承 AttentionControl 是一个很好的设计选择,它提供了更好的代码结构和可维护性。

AttentionControlAttentionStore 的差异

  1. 基础功能差异:
# AttentionControl - 基础抽象类
class AttentionControl(abc.ABC):
    def step_callback(self, x_t):
        return x_t  # 基础实现,直接返回输入

    def between_steps(self):
        return  # 空实现

    @abc.abstractmethod
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        raise NotImplementedError  # 必须由子类实现

# AttentionStore - 具体实现类
class AttentionStore(AttentionControl):
    def forward(self, attn, is_cross: bool, place_in_unet: str):
        # 具体实现:存储注意力值
        key = f"{place_in_unet}_{'cross' if is_cross else 'self'}"
        if attn.shape[1] <= 32**2:
            self.step_store[key].append(attn)
        return attn

    def between_steps(self):
        # 具体实现:合并注意力存储
        if len(self.attention_store) == 0:
            self.attention_store = self.step_store
        else:
            for key in self.attention_store:
                for i in range(len(self.attention_store[key])):
                    self.attention_store[key][i] += self.step_store[key][i]
        self.step_store = self.get_empty_store()
  1. 属性差异:
# AttentionControl 的属性
self.cur_step = 0
self.num_att_layers = -1
self.cur_att_layer = 0

# AttentionStore 额外添加的属性
self.step_store = self.get_empty_store()  # 存储当前步骤的注意力
self.attention_store = {}  # 存储累积的注意力
  1. 主要功能差异:

AttentionControl

  • 提供基础的注意力控制框架
  • 管理注意力层的计数和步骤
  • 定义抽象接口
  • 不存储任何注意力值

AttentionStore

  • 实现具体的注意力存储功能
  • 提供注意力值的累积和平均
  • 管理注意力值的存储结构
  • 添加了存储相关的属性和方法
  1. 新增方法:
# AttentionStore 特有的方法
@staticmethod
def get_empty_store():
    # 创建空的存储结构
    return {
        "down_cross": [], "mid_cross": [], "up_cross": [],
        "down_self": [], "mid_self": [], "up_self": []
    }

def get_average_attention(self):
    # 计算平均注意力
    return {
        key: [item / self.cur_step for item in self.attention_store[key]]
        for key in self.attention_store
    }

总结差异:

  1. 功能定位:

    • AttentionControl:基础框架,定义接口
    • AttentionStore:具体实现,专注于存储功能
  2. 实现程度:

    • AttentionControl:抽象类,部分方法为空实现
    • AttentionStore:具体类,所有方法都有完整实现
  3. 存储能力:

    • AttentionControl:不存储数据
    • AttentionStore:提供完整的存储和管理功能
  4. 使用场景:

    • AttentionControl:作为基类,定义规范
    • AttentionStore:实际使用,存储注意力值

这种设计体现了面向对象编程中的"抽象与具体"的关系,通过继承实现了代码的复用和扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2393129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式开发STM32 -- 江协科技笔记

1.背景介绍及基础认知 8大输入输出 斯密特触发器&#xff1a;高于设定阈值输出高电平&#xff0c;低于设定阈值输出低电平 有关上拉输入、下拉输入、推挽输出、开漏输出、复用开漏输出、复用推挽输出以及浮空输入、模拟输入的区别 1、上拉输入&#xff1a;上拉就是把电位拉高…

[网页五子棋][用户模块]客户端开发(登录功能和注册功能)

文章目录 客户端开发登录功能htmlcsscommon.csslogin.css jQuery引入 jquery 运行程序注册功能 客户端开发 登录功能 html <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport&…

MQTT协议,EMQX部署,MQTTX安装学习

一、MQTT概述 1.什么是MQTT MQTT是一种基于“发布订阅“”模式的消息传输协议。 消息&#xff1a;设备和设备之间传输的数据&#xff0c;或者服务和服务之间要传输的数据。 协议&#xff1a;传输数据时所遵循的规范。 2.常见的通讯模式 &#xff08;1&#xff09;客户端-服…

60天python训练计划----day40

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中展平操作&#xff1a;除第一个维度batchsize外全部展平dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropout 一.单通道图…

干泵,干式螺杆真空泵

干式真空泵&#xff1a; 无油干式机械真空泵&#xff08;又简称干式机械泵&#xff09;是指泵能从大气压力下开始抽气&#xff0c;又能将被抽气体直接排到大气中去&#xff0c;泵腔内无油或其他工作介质&#xff0c;而且泵的极限压力与油封式真空泵同等量级或者接近的机械真空泵…

Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(五):语音合成输出与交互增强

Tailwind CSS 实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;一&#xff09; Tailwind CSS 实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;二&#xff09;&#xff1a;实现交互功能 Tailwind CSS 实战&#xff0c;基于 Kooboo 构建 AI 对话框页面&#x…

职业本科院校无人机专业人才培养解决方案

2023年的中央经济工作会议强调了以科技创新推动现代化产业体系构建的重要性&#xff0c;并提出发展生物制造、商业航天、低空经济等战略性新兴产业。低空经济&#xff0c;依托民用无人机等低空飞行器&#xff0c;在多场景低空飞行活动的牵引下&#xff0c;正逐步形成一个辐射广…

软件评测机构如何保障质量?检测资质、技术实力缺一不可

软件评测机构在保障软件质量上起着关键作用&#xff0c;对软件行业的健康发展极为关键。它们采用专业的技术手段和严格的评估流程&#xff0c;对软件的运行效果、功能等多方面进行细致的审查&#xff0c;为开发者和使用者提供了客观、公正的参考依据。 检测资质正规软件评测机…

Linux多线程(六)之线程控制4【线程ID及进程地址空间布局】

文章目录 线程ID及进程地址空间布局线程局部存储 线程ID及进程地址空间布局 pthread_ create函数会产生一个线程ID&#xff0c;存放在第一个参数指向的地址中。 该线程ID和前面说的线程ID不是一回事。 前面讲的线程ID属于进程调度的范畴。 ​ 因为线程是轻量级进程&#xff…

1.什么是node.js、npm、vue

一、Node.js 是什么&#xff1f; &#x1f63a; 定义&#xff1a; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境&#xff0c;让你可以在浏览器之外运行 JavaScript 代码&#xff0c;主要用于服务端开发。 &#x1f63a;从计算机底层说&#xff1a;什么是“运…

Xamarin入门笔记(Xamarin已经被MAUI取代)

初级代码游戏的专栏介绍与文章目录-CSDN博客 Xamarin入门 概述 环境 Android开发环境比较简单&#xff0c;自带模拟器&#xff0c;实体机打开开发者模式即可。 iOS开发环境比较复杂&#xff0c;必须搭配Mac电脑&#xff0c;Windows连接Mac开发可能有问题&#xff08;比如发…

排查Oracle文件打开数过多

Oracle数据库在运行过程中&#xff0c;会打开大量的文件以执行其操作&#xff0c;包括数据文件、控制文件、日志文件等。如果Oracle用户打开的文件数过多&#xff0c;可能会引起系统性能下降。下面将深入分析Oracle用户文件打开数的优化策略&#xff0c;以帮助数据库管理员&…

应用层协议http(无代码版)

目录 认识URL urlencode 和 urldecode HTTP 协议请求与响应格式 HTTP 的请求方法 GET 方法 POST 方法 HTTP 的状态码 HTTP 常见 Header Location 关于 connection 报头 HTTP版本 远程连接服务器工具 setsockopt 我们来学习应用层协议http。 虽然我们说, 应用层协…

8.5 Q1|广州医科大学CHARLS发文 甘油三酯葡萄糖指数累积变化与 0-3期心血管-肾脏-代谢综合征人群中风发生率的相关性

1.第一段-文章基本信息 文章题目&#xff1a;Association between cumulative changes of the triglyceride glucose index and incidence of stroke in a population with cardiovascular-kidney-metabolic syndrome stage 0-3: a nationwide prospective cohort study 中文标…

无人机停机坪运行技术分析!

一、运行方式 1. 自动折叠与展开 部分停机坪采用二次折叠设计&#xff0c;通过传动组件实现自动折叠&#xff0c;缩小体积便于运输&#xff1b;展开后最大化停机面积&#xff0c;适应不同任务需求。例如&#xff0c;珠海双捷科技的专利通过两次折叠使停机坪体积最小化&#x…

【Java Web】速通HTML

参考笔记: JavaWeb 速通HTML_java html页面-CSDN博客 目录 一、前言 1.网页组成 1 结构 2 表现 3 行为 2.HTML入门 1 基本介绍 2 基本结构 3. HTML标签 1 基本说明 2 注意事项 4. HTML概念名词解释 二、HTML常用标签汇总 + 案例演示 1. 字体标签 font (1)定义 (2)案例 2…

在线制作幼教早教行业自适应网站教程

你想知道怎么做自适应网站吗&#xff1f;今天就来教你在线用模板做个幼教早教行业的网站哦。 首先得了解啥是自适应网站。简单说呢&#xff0c;自适应网站就是能自动匹配不同终端设备的网站&#xff0c;像手机、平板、电脑等。那如何做幼早教自适应网站呢&#xff1f; 在乔拓云…

Apptrace:APP安全加速解决方案

2021 年&#xff0c;某知名电商平台在 “618” 大促期间遭遇 DDoS 攻击&#xff0c;支付系统瘫痪近 2 小时&#xff1b;2022 年&#xff0c;一款热门手游在新版本上线时因 CC 攻击导致服务器崩溃。观察发现&#xff0c;电商大促、暑期流量高峰和年末结算期等关键商业周期&#…

Web攻防-SQL注入增删改查HTTP头UAXFFRefererCookie无回显报错

知识点&#xff1a; 1、Web攻防-SQL注入-操作方法&增删改查 2、Web攻防-SQL注入-HTTP头&UA&Cookie 3、Web攻防-SQL注入-HTTP头&XFF&Referer 案例说明&#xff1a; 在应用中&#xff0c;存在增删改查数据的操作&#xff0c;其中SQL语句结构不一导致注入语句…

GoldenDB管理节点zk部署

目录 1、准备阶段 1.1、部署规划 1.2、硬件准备 1.3、软件准备 1.4、网络端口开通 1.5、环境清理 2、实施阶段 2.1、操作系统配置 2.1.1、主机名修改 2.1.2、修改hosts文件 2.1.3、禁用防火墙 2.1.4、禁用selinux 2.1.5、禁用透明大页 2.1.6、资源限制调整 2.1.…