S4类[(子集)继承与附加参数

时间:2017-03-20 00:46:41

标签: r inheritance s4

这是Using callNextMethod() within accessor function in R的扩展。

更新2017-03-25:为了说明这只在加载方法时失败,而不是在构建的包中时,我创建了一个虚拟包:https://github.com/zkamvar/inheritest#readme

基本问题:

我有一个继承另一个类foo的类栏,它们都有[method]的附加参数。 foo的方法一致,但是第一次使用后bar的方法失败了。

错误和回溯:

Error in callNextMethod(x, i, j, ..., drop): bad object found as method (class "function")

4: stop(gettextf("bad object found as method (class %s)",  dQuote(class(method))), 
   domain = NA)
3: callNextMethod(x, i, j, ..., drop) at #9
2: .local(x, i, j, ..., drop = drop)
1: BAR["x"]

进一步详情:

我有一个包实现了一个依赖于另一个包中的类的类。构建包时,一切正常,但是当我的包被简单加载时(使用devtools::load_all(".")),我得到下面的行为。

最低工作范例:


foo <- setClass("foo", representation(x = "numeric", y = "numeric"))
bar <- setClass("bar", representation(distance = "numeric"), contains = "foo")

setMethod(f = "[", signature = signature(x = "foo", i = "ANY", j = "ANY", drop = "ANY"), 
  definition = function(x, i, j, ..., foo = TRUE, drop = FALSE) {
    if (foo) 
      message("FOOOOOOO")
    if (i == "x") {
      return(x@x)
    } else {
      if (i == "y") {
        return(x@y)
      }
    }
  })
#> [1] "["

setMethod(f = "[", signature = signature(x = "bar", i = "ANY", j = "ANY", drop = "ANY"), 
  definition = function(x, i, j, ..., bar = TRUE, drop = FALSE) {
    if (bar) 
      message("BAAAAAAR")
    if (i == "distance") {
      return(x@distance)
    } else {
      callNextMethod(x, i, j, ..., drop)
    }
  })
#> [1] "["

FOO <- new("foo", x = 1, y = 4)
BAR <- new("bar", x = 1, y = 4, distance = 3)
FOO["x"]
#> FOOOOOOO
#> [1] 1
BAR["x"]
#> BAAAAAAR
#> FOOOOOOO
#> [1] 1
FOO["x"]
#> FOOOOOOO
#> [1] 1
BAR["distance"]
#> BAAAAAAR
#> [1] 3
BAR["x"]  # fails
#> BAAAAAAR
#> Error in callNextMethod(x, i, j, ..., drop): bad object found as method (class "function")
BAR["x", foo = FALSE]
#> BAAAAAAR
#> [1] 1

注意:当我通过reprex传递此内容时,对BAR的第一次和最后一次调用也会导致错误,但我正在展示我在交互式会话中的体验。我正在使用R版本3.3.3

3 个答案:

答案 0 :(得分:3)

这是因为$_REQUEST['cheatsheetconfirm']不够聪明,无法处理带有增广形式的基元的方法。我已经修好了,并且很快就会进入行李箱。

答案 1 :(得分:1)

这是一个部分答案:它与“[”具体相关。这是一些工作代码,用'bat'方法替换'['方法。它适用于我:

foo <- setClass("foo", representation(x = "numeric", y = "numeric"))
bar <- setClass("bar", representation(distance = "numeric"), contains = "foo")

bat <- function (x, i, j, ..., drop = FALSE) message('in bat')
setGeneric('bat')
setMethod(f = "bat", signature = signature(x = "foo"), 
  definition = function(x, i, j, ..., foo = TRUE, drop = FALSE) {
    if (foo) 
      message("FOOOOOOO")
    if (i == "x") {
      return(x@x)
    } else {
      if (i == "y") {
        return(x@y)
      }
    }
  })
#> [1] "["

setMethod(f = "bat", signature = signature(x = "bar"), 
  definition = function(x, i, j, ..., bar = TRUE, drop = FALSE) {
    if (bar) 
      message("BAAAAAAR")
    if (i == "distance") {
      return(x@distance)
    } else {
      callNextMethod(x, i, j, ..., drop)
    }
  })

FOO <- new("foo", x = 1, y = 4)
BAR <- new("bar", x = 1, y = 4, distance = 3)
bat(FOO, 'x')
bat(BAR, 'distance')
bat(BAR, 'x')

现在:

bat(FOO, 'x')
FOOOOOOO
[1] 1
bat(BAR, 'x')
BAAAAAAR
FOOOOOOO
[1] 1
bat(BAR, 'distance')
BAAAAAAR
[1] 3
bat(BAR, 'x')
BAAAAAAR
FOOOOOOO
[1] 1

所以,我认为这与S4调度和[自己的调度......和解决方案的相互作用有关)?我没有,除了避免像瘟疫一样的S4。也许R-devel可以提供帮助。这可能是一个真正的R bug,因为代码只会中断[

答案 2 :(得分:1)

问题可能与[是基元的事实有关,而使用S4时基元的处理方式也不同。深入研究callNextMethod表明,在该方法与该原始函数的泛型相比具有不同的参数的情况下,没有正确分析callstack。如果从方法定义中删除参数bar,则调度可以正常工作。

尽管如此,还有另一种解决方法并不需要您选择其他功能名称。我添加了一个额外的函数as.foo并在转换为foo对象后调用泛型:

setGeneric("as.foo", function(x)standardGeneric("as.foo"))
setMethod("as.foo", signature = "bar",
          function(x)
            new("foo", x = x@x, y = x@y))

setMethod(f = "[", signature = signature(x = "bar", i = "ANY", j = "ANY", drop = "ANY"), 
          definition = function(x, i, j, ..., bar = TRUE, drop = FALSE) {
            if (bar) 
              message("BAAAAAAR")
            if (i == "distance") {
              return(x@distance)
            } else {
               x <- as.foo(x)
               callGeneric()
            }
          }
)

通过这种方式,您可以绕过调度中的打嗝,以及以前失败的所有代码现在都可以使用

FOO["x"]
#> FOOOOOOO
#> [1] 1
BAR["x"]
#> BAAAAAAR
#> FOOOOOOO
#> [1] 1
BAR["distance"]
#> BAAAAAAR
#> [1] 3
BAR["x"]  
#> BAAAAAAR
#> FOOOOOOO
#> [1] 1
BAR["x", foo = FALSE]
#> BAAAAAAR
#> [1] 1