实际问题


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类的所有实例都从其他超类继承,例如.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"’


广告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中的非正式/正式类和方法分派的交互,R6类的实现是否存在矛盾并没有引起我一个问题。

最佳答案

由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而言,这意味着对于您的程序包,您需要确保希望S4方法对其起作用的所有R6类都以这种方式调用setOldClass()

这可以通过将以下调用绑定在函数.onLoad().onAttach()中来完成(请参见here):

.onLoad <- function(libname, pkgname) {
  setOldClass(c("TestR6_1", "R6"))
  setOldClass(c("TestR6_2", "R6"))
  setOldClass(c("TestR6_3", "R6"))
}


假设您已经定义了三个R6类(TestR6_1TestR6_3

08-19 23:32