理解 attr(x, "class") 和 class(x) 之间的区别

Understanding difference between attr(x, "class") and class(x)

背景

我正在查看 Romain François 提出的 jazz 包。 Romain 使用以下语法定义函数 is_bare_vector

is_bare_vector <- function(x) {
  is_vector(x) && !is.object(x) && is.null(attr(x, "class"))
}

问题

对于:x <- 1:

attr(x, "class")
# NULL

鉴于:

class(x)
# [1] "numeric"

我想了解为什么这两个函数提供不同的答案? ?attr 上的帮助参考 ?class

?attr

Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set. (Note that this is not true of levels which should be set for factors via the levels replacement function.)

?class

Many R objects have a class attribute, a character vector giving the names of the classes from which the object inherits. (Functions oldClass and oldClass<- get and set the attribute, which can also be done directly.)

您只需要在 help("class") 中进一步阅读:

If the object does not have a class attribute, it has an implicit class, notably "matrix", "array", "function" or "numeric" or the result of typeof(x) (which is similar to mode(x))

显然,如果 class 属性是 NULLclass() 也会 return 隐含的 class;让我们检查一下 C source code -- 我们看到如果 class 属性的长度为 0,它会得到 implicit class.:

SEXP R_data_class(SEXP obj, Rboolean singleString)
{
    SEXP value, klass = getAttrib(obj, R_ClassSymbol);
    int n = length(klass);
    if(n == 1 || (n > 0 && !singleString))
    return(klass);
    if(n == 0) {
    SEXP dim = getAttrib(obj, R_DimSymbol);
    int nd = length(dim);
    if(nd > 0) {
        if(nd == 2)
        klass = mkChar("matrix");
        else
        klass = mkChar("array");
    }
    else {
      SEXPTYPE t = TYPEOF(obj);
      switch(t) {
      case CLOSXP: case SPECIALSXP: case BUILTINSXP:
        klass = mkChar("function");
        break;
      case REALSXP:
        klass = mkChar("numeric");
        break;
      case SYMSXP:
        klass = mkChar("name");
        break;
      case LANGSXP:
        klass = lang2str(obj, t);
        break;
      default:
        klass = type2str(t);
      }
    }
    }
    else
    klass = asChar(klass);
    PROTECT(klass);
    value = ScalarString(klass);
    UNPROTECT(1);
    return value;
}