R6类继承自(非正式S3)类R6
的事实是否允许为该类的签名参数定义S4方法?
因为这是 - AFAICT - 不是这样,什么是符合当前S3 / S4标准的解决方法,或者在某些情况下可能被视为“最佳实践”?
参考类
考虑以下示例,您希望定义在超类上调度Reference Classes的所有实例都从(envRefClass
)继承的方法:
TestRefClass <- setRefClass("TestRefClass", fields= list(.x = "numeric"))
setGeneric("foo", signature = "x",
def = function(x) standardGeneric("foo")
)
setMethod("foo", c(x = "envRefClass"),
definition = function(x) {
"I'm the method for `envRefClass`"
})
> try(foo(x = TestRefClass$new()))
[1] "I'm the method for `envRefClass`"
这种继承结构并不直接明显,因为class()
不会揭示这一事实:
class(TestRefClass$new())
[1] "TestRefClass"
attr(,"package")
[1] ".GlobalEnv"
但是,查看类生成器对象的属性会显示它:
> attributes(TestRefClass)
[... omitted ...]
Reference Superclasses:
"envRefClass"
[... omitted ...]
这就是调度工作的原因
R6课程
当你想为R6类做类似的事情时,事情似乎并不是直截了当的,即使它们最初是这样的(与参考类相比):
TestR6 <- R6Class("TestR6", public = list(.x = "numeric"))
setMethod("foo", c(x = "R6"),
definition = function(x) {
"I'm the method for `R6`"
})
> try(foo(x = TestR6$new()))
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘foo’ for signature ‘"TestR6"’
通过“直接显示”,我的意思是class()
实际上表明所有R6类都继承自类R6
,可以用作方法调度的超类:
class(TestR6$new())
[1] "TestR6" "R6"
R6Class()
的帮助页面实际上显示,只要R6
,类class = TRUE
仅作为非正式的S3类添加。这也是为什么在尝试为这个类定义S4方法时会出现警告的原因。
那么这基本上给我们留下了两个可能的选项/解决方法:
R6
setOldClass()
变成正式课程
.R6
广告1)
setOldClass("R6")
> isClass("R6")
[1] TRUE
当在类表/图形中以S3样式进行黑客攻击时,这是有效的:
dummy <- structure("something", class = "R6")
> foo(dummy)
[1] "I'm the method for `R6`"
但是,实际的R6类实例失败了:
> try(foo(x = TestR6$new()))
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘foo’ for signature ‘"TestR6"’
Ad 2)
.R6 <- R6Class(".R6")
TestR6_2 <- R6Class("TestR6_2", inherit = .R6, public = list(.x = "numeric"))
setMethod("foo", c(x = ".R6"),
definition = function(x) {
"I'm the method for `.R6`"
})
> try(foo(x = TestR6_2$new()))
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘foo’ for signature ‘"TestR6_2"’
虽然方法1排序在“灰色区域”中运行以使S3和S4稍微兼容,但是方法2似乎是IMO应该工作的完全有效的“纯S4”解决方案。如果R6类的实现与R中的非正式/正式类和方法调度的交互存在不一致,则不会引起我的提问。
答案 0 :(得分:6)
由Hadley Wickham提供我发现setOldClass()
实际上解决了包含继承结构时的问题:
require("R6")
setOldClass(c("TestR6", "R6"))
TestR6 <- R6Class("TestR6", public = list(.x = "numeric"))
setGeneric("foo", signature = "x",
def = function(x) standardGeneric("foo")
)
setMethod("foo", c(x = "R6"),
definition = function(x) {
"I'm the method for `R6`"
})
try(foo(x = TestR6$new()))
但是,AFAICT,这意味着对于您的软件包,您需要确保以这种方式调用{em>所有您想要用于其中的R6类的<{1}}工作方法。
可以通过在函数setOldClass()
或.onLoad()
中捆绑这些调用来完成此操作(请参阅here):
.onAttach()
这假设您已经定义了三个R6类(.onLoad <- function(libname, pkgname) {
setOldClass(c("TestR6_1", "R6"))
setOldClass(c("TestR6_2", "R6"))
setOldClass(c("TestR6_3", "R6"))
}
到TestR6_1
)