is和inherits有什么区别?涉及IT技术。

33

如果我想检查一个变量是否继承自某个类,我可以使用isinherits

class(letters)
## [1] "character"
is(letters, "character")
## [1] TRUE
inherits(letters, "character")
## [1] TRUE

有没有偏好,我应该使用哪一个,它们是否会返回不同的值?


@Richie,既然你提出了这个问题并且回答了它,能否在你的问题/回答中包含 "character" %in% class(letters)?根据这篇文章我理解它相当于inherits,只不过速度较慢,但我不确定。 - moodymudskipper
@Moody_Mudskipper 是的,那也可以,不过代码的意图不太清晰,所以我不确定你何时想要使用它。 - Richie Cotton
我在这个答案中举了例子:https://dev59.com/_2Ml5IYBdhLWcg3wCDGW 。is.list(iris)TRUE,但"list" %in% class(iris)inherits(iris,"list")FALSE,这对于区分数据框和列表很有用。我是否应该理解,在所有情况下都建议使用inherits,而后两者实际上是等价的?顺便说一句,你不错的datacamp课程把我带到了这里。 - moodymudskipper
2个回答

14

简短版:

使用 inherits,但在处理数字和 S4 类时要小心。


详细版:

is 帮助页面的“另请参阅”部分:

对于 S4 和非 S4 对象,inherits 几乎总是等价于 is,并且速度略快。不相等适用于具有条件超类的类,在关系(不常见且不鼓励)中存在非平凡测试的类:对于这些类,is 测试关系,但根据定义,inherits 忽略 S4 对象的条件继承。

来自 inherits 帮助页面的“正式类”部分:

正式类的 inherits 类比为 is。这两个函数行为一致,除了一个例外:S4 类可以具有条件继承和显式测试。在这种情况下,is 将测试条件,但 inherits 忽略所有条件超类。

因此,它们大多返回相同的结果,但 inherits 更快,因此在大多数情况下应该是默认选择。(正如 Konrad 所提到的,is 还需要加载 methods 包,这可能使其不适合对性能敏感的 Rscript 使用场景。)

如果您正在使用具有条件继承的 S4 类,则这两个函数的值可能会有所不同,但是这是 不推荐的(请参见“方法选择和分派:详细信息”部分),因此希望很少发生。

两个函数最明显不同之处在于检查整数是否为数字时。

class(1L)
## [1] "integer"
is.numeric(1L)
## [1] TRUE
is(1L, "numeric")
## [1] TRUE
inherits(1L, "numeric")
## [1] FALSE

1
好的,那么你能解释一下为什么整数不会“继承”,而浮点数会吗?Rgames> class(1.4) [1] "numeric" Rgames> is.numeric(1.4) [1] TRUE Rgames> inherits(1.4,'numeric') [1] TRUE Rgames> is(1.4,'numeric') [1] TRUE - Carl Witthoft
2
我认为这可能与数字对象仅具有隐式类有关:来自?class,如果对象没有类属性,则具有隐式类,“"matrix"”,“"array"”或‘mode(x)’的结果(除了整数向量具有隐式类“"integer"”)。我可以想象(??)is查看隐式类而inherits不会...? - Ben Bolker
我记得听到高R学科的人称这个为“不幸”。 - Dieter Menne
3
另一个非常重要的区别是 is 函数在 methods 包中,而该包默认情况下在运行 Rscript 时不会加载(因为加载速度较慢)。相比之下,inherits 函数属于 base 包,因此在 R 脚本程序中随时可用。 - Konrad Rudolph

1
除了is()inherits(),我们还可以使用is.*()测试特定类型的对象。这三个函数可能会返回不同的结果。根据这个答案,我做了以下操作:
  • 创建了许多不同类型的R对象。
  • 使用storage.mode()mode()typeof()class()提取了这些对象的类型。
  • 使用is()inherits()is.*()测试这些对象是否属于返回的类型。
下面是上述三个步骤的一个小例子:
# Get object classes withs torage.mode(), mode(), typeof() and class().
obj <- logical()
(types <- c(storage.mode= storage.mode(obj),
            mode= mode(obj),
            type= typeof(obj),
            class= class(obj)))
storage.mode         mode         type        class
    "double"    "numeric"     "double"    "numeric"

# Test returned types with is, inhertis and is.*.
> is(obj, "double"); is(obj, "numeric")
[1] FALSE
[1] TRUE
> inherits(obj, "double"); inherits(obj, "numeric")
[1] FALSE
[1] TRUE
> is.double(obj); is.numeric(obj)
[1] TRUE
[1] TRUE

现在我们使用以下代码对一系列对象类型执行此操作:
# Generate objects of different types.
library(xml2)
setClass("dummy", representation(x="numeric", y="numeric"))
obj <- list(
  "logical vector" = logical(),
  "integer vector" = integer(),
  "numeric vector" = numeric(),
  "complex vector" = complex(),
  "character vector" = character(),
  "raw vector" = raw(),
  "factor" = factor(),
  "logical matrix" = matrix(logical()),
  "numeric matrix" = matrix(numeric()),
  "logical array" = array(logical(8), c(2, 2, 2)),
  "numeric array" = array(numeric(8), c(2, 2, 2)),
  "list" = list(),
  "pairlist" = .Options,
  "data frame" = data.frame(),
  "closure function" = identity,
  "builtin function" = `+`,
  "special function" = `if`,
  "environment" = new.env(),
  "null" = NULL,
  "formula" = y ~ x,
  "expression" = expression(),
  "call" = call("identity"),
  "name" = as.name("x"),
  #"paren in expression" = expression((1))[[1]], # Code fails with this
  #"brace in expression" = expression({1})[[1]], # Code fails with this
  "S3 lm object" = lm(dist ~ speed, cars),
  "S4 dummy object" = new("dummy", x = 1:10, y = rnorm(10)),
  "external pointer" = read_xml("<foo><bar /></foo>")$node
  )

# Extract types and test them.
res <- do.call("rbind.data.frame", Map(function(x, name){
  types <- c(storage.mode= storage.mode(x),
                mode= mode(x),
                type= typeof(x),
                class= class(x))
  data.frame("object"= name,
             "extract_method"= names(types),
             "extract_result"= types,
             "inherits"= sapply(types, function(i) inherits(x, i)),
             "is"= sapply(types, function(i) is(x, i)),
             "is.type"= sapply(types, function(i) eval(parse(text= paste0("tryCatch({is.", i, "(x)}, error= function(e){'is.", i, "() does not exist'})"))))
  )}, obj, names(obj)))
rownames(res) <- 1:nrow(res)
res <- res[order(res$extract_method), ]

我们可以从结果res中得到一些见解。例如,我们可以看到is.()inherits()不返回相同的结果:
> res[res$inherits != res$is, ]
           object extract_method extract_result inherits   is is.type
6  integer vector           mode        numeric    FALSE TRUE TRUE
87           call   storage.mode       language    FALSE TRUE TRUE
89           call           type       language    FALSE TRUE TRUE

当然,结果显示的内容还有很多,例如我们可以看到在三种提取方法中返回的类型中,inherits() 返回 FALSE 的地方等等。我在这里省略了这些内容。实际上,我认为我的答案更加全面,因为它考虑了提取和测试对象类型之间的差异。在阅读了关于对象类型的大量资料后,我得出了上面的代码并想分享给大家。但是,仅使用 res[res$inherits != res$is, ] 就可以回答问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接