当前位置: 首页 > article >正文

【Python 进阶2】抽象方法和实例调用方法

抽象方法和实例调用方法

对比表格:

特性抽象方法 (forward)实例调用方法 (call)
定义方式@abc.abstractmethod 装饰器特殊方法名 __call__
调用方式不能直接调用,必须通过子类实现可以直接调用对象:controller(attn, ...)
实现要求必须由子类实现,否则无法实例化可以在基类中实现,子类可以覆盖
主要功能定义具体业务逻辑(如存储注意力)处理通用流程(如计数、步骤控制)
代码位置在子类中实现(如 AttentionStore在基类中实现(AttentionControl
使用场景需要子类提供不同实现时需要统一接口时
错误处理在类定义时检查在运行时检查
代码复用每个子类都需要实现所有子类共享基类实现

以下面代码片段为例

class AttentionControl(abc.ABC):def step_callback(self, x_t):return x_tdef between_steps(self):return@propertydef num_uncond_att_layers(self):return 0@abc.abstractmethoddef forward(self, attn, is_cross: bool, place_in_unet: str):raise NotImplementedErrordef __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 += 1if self.cur_att_layer == self.num_att_layers + self.num_uncond_att_layers:self.cur_att_layer = 0self.cur_step += 1self.between_steps()return attndef reset(self):self.cur_step = 0self.cur_att_layer = 0def __init__(self):self.cur_step = 0self.num_att_layers = -1self.cur_att_layer = 0
  1. 抽象方法:

    • 是接口定义
    • 强制子类实现
    • 提供具体功能
  2. 实例调用方法:

    • 是统一接口
    • 处理通用逻辑
    • 调用抽象方法

具体的调用差异

forward__call__ 的区别和调用时机:

  1. 基本区别:
# forward 是抽象方法,需要子类实现
@abc.abstractmethod
def forward(self, attn, is_cross: bool, place_in_unet: str):raise NotImplementedError# __call__ 是实例调用方法,可以直接调用对象
def __call__(self, attn, is_cross: bool, place_in_unet: str):# 调用 forward 并处理其他逻辑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)# ... 其他逻辑
  1. 调用时机:

__call__ 的调用:

controller = AttentionStore()
# 直接调用对象时会触发 __call__
result = controller(attn, is_cross=True, place_in_unet="down")

forward 的调用:

# forward 是在 __call__ 内部被调用的
# 不会直接调用 forward
  1. 工作流程:
# 1. 当调用 controller(attn, ...) 时,会触发 __call__
# 2. __call__ 方法会:
#    - 检查条件
#    - 调用 forward
#    - 更新计数器
#    - 处理步骤间逻辑
# 3. forward 方法(由子类实现)处理具体的注意力计算
  1. 实际应用示例:
# 在 AttentionStore 中
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# 在 AttentionReplace 中
def forward(self, attn, is_cross: bool, place_in_unet: str):# 替换注意力值return torch.einsum("hpw,bwn->bhpn", attn_base, self.mapper)
  1. 为什么这样设计:

a) 分离关注点:

  • __call__ 处理通用逻辑(计数、步骤等)
  • forward 处理具体实现(存储、替换等)

b) 提供统一接口:

  • 所有控制器都可以像函数一样调用
  • 内部实现可以不同

c) 代码复用:

  • 通用逻辑在 __call__ 中实现一次
  • 子类只需要实现 forward
  1. 调用顺序:
# 1. 外部调用
controller(attn, is_cross=True, place_in_unet="down")# 2. 触发 __call__
def __call__(self, attn, is_cross: bool, place_in_unet: str):# 3. 条件检查if self.cur_att_layer >= self.num_uncond_att_layers:# 4. 调用 forwardattn[h // 2 :] = self.forward(attn[h // 2 :], is_cross, place_in_unet)# 5. 处理其他逻辑

这种设计的好处是:

  1. 提供了清晰的接口
  2. 分离了通用逻辑和具体实现
  3. 方便代码复用和维护
  4. 使代码结构更加清晰

所以,在这个例子中 forward__call__ 的区别在于:

  • __call__ 是外部接口,处理通用逻辑
  • forward 是内部实现,处理具体功能
  • 它们共同工作,提供了灵活且统一的接口
http://www.lryc.cn/news/2397093.html

相关文章:

  • 第1章:走进Golang
  • Predixy的docker化
  • C++ 之 多态 【虚函数表、多态的原理、动态绑定与静态绑定】
  • 【JavaWeb】Maven、Servlet、cookie/session
  • [蓝桥杯]阶乘求值【省模拟赛】
  • 鸿蒙OSUniApp微服务架构实践:从设计到鸿蒙部署#三方框架 #Uniapp
  • Rust 编程实现猜数字游戏
  • 关于神经网络中的激活函数
  • CentOS_7.9 2U物理服务器上部署系统简易操作步骤
  • 第十三篇:MySQL 运维自动化与可观测性建设实践指南
  • 短视频平台差异视角下开源AI智能名片链动2+1模式S2B2C商城小程序的适配性研究——以抖音与快手为例
  • HTTP 如何升级成 HTTPS
  • 【笔记】Windows 下载并安装 ChromeDriver
  • Spark-Core Project
  • SQL 中的 `CASE WHEN` 如何使用?
  • Wireshark 使用教程:让抓包不再神秘
  • JWT安全:接收无签名令牌.【签名算法设置为none绕过验证】
  • 什么算得到?什么又算失去?
  • 白银价格查询接口如何用Java进行调用?
  • FreeBSD 14.3 候选版本附带 Docker 镜像和关键修复
  • NodeJS全栈WEB3面试题——P6安全与最佳实践
  • 「Java教案」算术运算符与表达式
  • Ubuntu开机黑屏现象的常见解决方案详解
  • 黑马程序员C++核心编程笔记--4 类和对象--多态
  • 论文写作核心要点
  • [java]eclipse中windowbuilder插件在线安装
  • Ubuntu20.04服务器开启路由转发让局域网内其他电脑通过该服务器连接外网
  • 【Oracle】TCL语言
  • Windows | 总误按Num Lock?修改注册表永久禁用Numlk键使小键盘一直输入数字
  • 2.RV1126-OPENCV Mat理解和AT函数