LISP - 类优先级
Lisp - 理解 CLOS 中的类优先级
Section titled “Lisp - 理解 CLOS 中的类优先级”The Common Lisp 对象系统 (CLOS) 具有强大的多重继承系统。当一个类从多个父类(超类)继承时,可能会出现潜在的歧义:如果多个超类提供了同名的方法,应该使用哪一个?**类优先级(Class Precedence)**通过为每个类定义一个单一的、线性的继承顺序来解决这种歧义。
确定类优先级列表(CPL)
Section titled “确定类优先级列表(CPL)”CLOS 使用复杂的算法(C3 方法解析顺序)为每个类计算一个类优先级列表 (CPL)。CPL 决定了完整的继承层次结构。规则可以简化如下:
- 一个类总是比其超类具有更高的优先级。
- 在
defclass表单中列出的直接超类的顺序会被保留。 - 最终列表是整个类层次结构的一致的、确定性的线性化。
- 内置类
standard-object和t始终位于列表末尾,提供一个共同的根。
您可以使用函数 (class-precedence-list (find-class 'class-name)) 检查任何类的 CPL。为了获得更易读的输出,我们可以将 class-name 映射到结果上。
示例:多重继承顺序
Section titled “示例:多重继承顺序”让我们看看 defclass 中超类的顺序如何影响 CPL。
main.lisp
Section titled “main.lisp”;;; main.lisp - 演示类优先级
(defclass a () ())(defclass b () ())
;; 类 C 继承自 A,然后是 B(defclass c (a b) ())
;; 类 D 继承自 B,然后是 A(defclass d (b a) ())
(defun show-cpl (class-symbol) "打印给定类符号的类优先级列表。" (format t "CPL for ~a: ~a~%" class-symbol (mapcar #'class-name (ccl:class-precedence-list (find-class class-symbol)))))
(show-cpl 'a)(show-cpl 'b)(show-cpl 'c)(show-cpl 'd)注意: class-precedence-list 函数不是 ANSI 标准导出的。大多数实现都在 ccl、sb-mop 或 clos 等包中提供它。我们在这里使用 ccl:class-precedence-list 作为示例。
当您执行此代码时,输出清晰地显示了线性化顺序:
CPL for A: (A STANDARD-OBJECT T)CPL for B: (B STANDARD-OBJECT T)CPL for C: (C A B STANDARD-OBJECT T)CPL for D: (D B A STANDARD-OBJECT T)- 对于
c,由于它定义为(defclass c (a b) ()),其 CPL 为(C A B ...)。类A优先于B。 - 对于
d,定义为(defclass d (b a) ()),其 CPL 为(D B A ...)。类B优先于A。
实际影响:方法分派
Section titled “实际影响:方法分派”CPL 的主要目的是在调用泛型函数(generic function)时确定执行哪个方法。CLOS 会找到给定参数的所有适用方法,并根据 CPL 确定并执行最特定的一个。
方法分派示例
Section titled “方法分派示例”;; 定义一个泛型函数(defgeneric identify (obj))
;; 定义类 A 和类 B 的方法(defmethod identify ((obj a)) "为类 A 特化的方法")
(defmethod identify ((obj b)) "为类 B 特化的方法")
;; 创建 C 和 D 的实例(let ((instance-c (make-instance 'c)) (instance-d (make-instance 'd)))
(format t "Calling identify on an instance of C: ~s~%" (identify instance-c)) (format t "Calling identify on an instance of D: ~s~%" (identify instance-d)))Calling identify on an instance of C: "Method specialized for class A"Calling identify on an instance of D: "Method specialized for class B"- 对于
instance-c,identify的两个方法(针对a和b)都适用。由于在C的 CPL 中A位于B之前,因此选择A的方法作为最特定的方法。 - 对于
instance-d,情况则相反。在D的 CPL 中B位于A之前,因此选择B的方法。
- 可预测性: 类优先级提供了一种可预测且强大的机制,用于解决复杂继承层次结构中的方法冲突。
- 控制: 作为开发者,您可以通过在
defclass中排列超类的顺序来控制优先级。 - 调试: 当不确定方法行为时,检查
class-precedence-list是调试的第一步。 - 高级方法: 这是更高级功能的基础,例如方法组合(例如
:before、:after和:around方法),它们允许多个方法对单个泛型函数调用做出贡献。