R6类的S4调度行为不一致 [英] Inconsistency of S4 dispatch behavior for R6 classes

查看:217
本文介绍了R6类的S4调度行为不一致的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


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

  1. Shouldn't the fact that R6 classes inherit from (informal S3) class R6 allow the definition of S4 methods for signature arguments of that very class?

因为这是--AFAICT - 不是这样,那么符合当前S3 / S4标准的解决方法是什么或者在某些情况下可能会被视为最佳做法?

As this is - AFAICT - not the case, what would be a workaround that is in line with current S3/S4 standards or that could somewhat be regarded as "best practice" in such situations?



背景和示例



参考类

考虑以下示例,您要在其中定义在超类上分派的方法 参考类的所有实例 继承自( envRefClass ):

Consider the following example where you would like to define methods that dispatch on the superclass that all instances of Reference Classes inherit from (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()不会显示这一事实:

This inheritance structure is not directly obvious as class() won't reveal that fact:

class(TestRefClass$new())
[1] "TestRefClass"
attr(,"package")
[1] ".GlobalEnv"

然而,看一下类生成器对象的属性就会显示出来:

However, a look at the attributes of the class generator object reveals it:

> attributes(TestRefClass)
[... omitted ...]

 Reference Superclasses:  
    "envRefClass"

[... omitted ...]

这就是调度工作的原因

R6课程

当你想为R6课程做类似的事情时,事情似乎并不是直截了当的,即使他们最初如此出现(与参考类相比):

When you would like to a similar thing for R6 classes, things don't seem to be straight forward even though they initially appear so (compared to Reference Classes):

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 ,它们可以用作方法调度的超类:

By "appearing straight forward" I mean that class() actually suggests that all R6 classes inherit from class R6 that could be used as superclass for method dispatch:

class(TestR6$new())
[1] "TestR6" "R6"  

R6Class()的帮助页面实际上显示该类 R6 只要 class = TRUE ,它就会被添加为非正式的S3类。这也是为什么在尝试为这个类定义S4方法时会出现警告的原因。

The help page of R6Class() actually reveals that class R6 is merely added as an informal S3 class as long as class = TRUE. That's also why there is a warning when trying to define a S4 method for this class.

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

So then this basically leaves us with two possible options/workarounds:


  1. 转班 R6 通过进入正式课程setOldClass()

  2. 拥有R6课程的所有实例在来自其他超类的herit,比如说, .R6

  1. Turn class R6 into a formal class via setOldClass()
  2. Have all instances of R6 classes inherit from some other superclass, say, .R6

广告1)

setOldClass("R6")
> isClass("R6")
[1] TRUE

这在黑客入侵时有效类表/图表中的S3样式:

This works when hacking away in an S3 style at the class table/graph:

dummy <- structure("something", class = "R6")
> foo(dummy)
[1] "I'm the method for `R6`"

但是,实际的R6类实例失败了:

However, it fails for actual R6 class instances:

> 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解决方案。事实上,如果存在不一致的话,它不会让我提出问题关于R中非正式/正式类和方法调度的交互的R6类的实现。

Conclusion

While approach 1 sort operates in a "grey area" to make S3 and S4 somewhat compatible, approach 2 seems like a perfectly valid "pure S4" solution that IMO should work. The fact that it's not brought me to raising the question if there exists an inconsistency in the implementation of R6 classes with respect to the interaction of informal/formal classes and method dispatch in R.

推荐答案

由Hadley Wickham提供I发现 setOldClass()实际上解决了包含继承结构时的问题:

Courtesy of Hadley Wickham I found out that setOldClass() in fact solves the problem when including the inheritance structure:

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类。

However, AFAICT, this implies that for your packages, you need to make sure that setOldClass() is called in that way for all of your R6 classes for which you would like your S4 methods to work.

这可以通过在函数 .onLoad() .onAttach()中捆绑这些调用(参见 where ):

This could be done by bundling these calls in function .onLoad() or .onAttach() (see here):

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

这假设你已经定义了三个R6类( TestR6_1 Te stR6_3

This is supposing that you have defined three R6 classes (TestR6_1 through TestR6_3)

这篇关于R6类的S4调度行为不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆