4

实际问题

  1. R6类继承自(非正式 S3)类的事实不应该R6允许为该类的签名参数定义 S4 方法吗?

  2. 因为这是 - AFAICT - 不是这种情况,在这种情况下,符合当前 S3/S4 标准或在某种程度上可以被视为“最佳实践”的解决方法是什么?

背景和例子

参考类

考虑以下示例,您希望定义在所有引用类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只是作为一个非正式的 S3 类添加只要class = TRUE。这也是尝试为此类定义 S4 方法时出现警告的原因。

那么这基本上给我们留下了两种可能的选择/解决方法:

  1. 通过将课堂R6变成正式课堂setOldClass()
  2. 让 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 类的实现在 R 中非正式/正式类的交互和方法分派方面存在不一致,这并没有让我提出这个问题。

4

1 回答 1

8

感谢 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,这意味着对于您的包,您需要确保setOldClass()以这种方式为您希望 S4 方法工作的所有R6 类调用它。

这可以通过将这些调用捆绑在函数中来完成,.onLoad()或者.onAttach()(参见此处):

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

这是假设您已经定义了三个 R6 类(TestR6_1通过TestR6_3

于 2014-12-09T17:52:11.440 回答