如何解决自定义S3类myclass与其他类的组泛型冲突
自定义S3类
myclass与其他类运算的冲突解决 问题背景
我们实现了一个自定义S3类myclass,并通过组泛型Ops.myclass确保基础运算后保留类属性:
myvec <- 1:5 class(myvec) <- "myclass" Ops.myclass <- function(e1, e2) { if(is(e1, "myclass")) { e1 <- unclass(e1) structure(NextMethod(), class="myclass") } else if(is(e2, "myclass")) { e2 <- unclass(e2) structure(NextMethod(), class="myclass") } }
该实现对基础类型运算正常(如1 + myvec仍返回myclass),但与自带专属运算符方法的对象(如Sys.Date())运算时,会出现警告且结果不符合预期:
myvec + Sys.Date() # 输出: # [1] 19454 19455 19456 19457 19458 # attr(,"class") # [1] "myclass" # Warning message: # Incompatible methods ("Ops.myclass", "+.Date") for "+"
而取消myclass类后,运算结果是正确的日期类型:
unclass(myvec) + Sys.Date() # [1] "2023-04-07" "2023-04-08" "2023-04-09" "2023-04-10" "2023-04-11"
问题原因
- S3组泛型在遇到两个对象都有对应运算符方法时,会触发方法冲突警告;
- 原
Ops.myclass强制将运算结果转为myclass类,覆盖了Date类的运算逻辑(+.Date本应返回Date类型,却被强制转为数值型的myclass)。
解决方案
修改Ops.myclass方法,优先判断操作对象的另一方是否有专属运算符方法:如果有,则取消自身的myclass类,让对方的方法主导运算;仅当另一方为基础类型时,才保留myclass类属性。
修改后的代码:
Ops.myclass <- function(e1, e2) { # 获取当前运算符(如"+"、"-"等) op <- .Generic # 检查另一方是否有专属的运算符方法 e1_has_op <- !is.null(getS3method(op, class(e1), optional = TRUE)) e2_has_op <- !is.null(getS3method(op, class(e2), optional = TRUE)) # 若另一方有专属方法,放弃myclass类,让对方方法处理 if (is(e1, "myclass") && e2_has_op) { return(do.call(op, list(unclass(e1), e2))) } else if (is(e2, "myclass") && e1_has_op) { return(do.call(op, list(e1, unclass(e2)))) } # 基础类型运算,保留myclass类 if (is(e1, "myclass")) { e1 <- unclass(e1) structure(NextMethod(), class = "myclass") } else if (is(e2, "myclass")) { e2 <- unclass(e2) structure(NextMethod(), class = "myclass") } }
验证效果
现在执行与Date对象的运算,结果正常且无警告:
myvec + Sys.Date() # [1] "2023-04-07" "2023-04-08" "2023-04-09" "2023-04-10" "2023-04-11" # 基础类型运算仍保留myclass类 class(myvec + 1) # [1] "myclass"
内容的提问来源于stack exchange,提问作者Karolis Koncevičius
相关产品推荐
相关产品推荐

