如何在 R 中创建不打印属性(不复制对象)的打印方法?

dhe*_*rsz 6 printing methods copy r

我正在 R 中创建一个自定义 S3 对象,但是当我打印它时,属性也会被打印。例如:

x <- 1:3
x <- structure(x, class = "myclass")
print(x)
#> [1] 1 2 3
#> attr(,"class")
#> [1] "myclass"
Run Code Online (Sandbox Code Playgroud)

公平地说,我还没有添加print方法。SO 和其他地方的许多答案建议从对象中删除属性,然后打印它,如下所示(也以不可见的方式返回对象以保持原始print属性):

x <- 1:3
x <- structure(x, class = "myclass")

print.myclass <- function(x, ...) {
  print(unclass(x), ...)
  return(invisible(x))
}

print(x)
#> [1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

然而,这种方法会创建 的副本x,而我不希望这样。我们可以看到tracemem()

x <- 1:3
x <- structure(x, class = "myclass")
tracemem(x)
#> [1] "<0x558e1a9adcc8>"

print.myclass <- function(x, ...) {
  print(unclass(x), ...)
  return(invisible(x))
}

print(x)
#> tracemem[0x558e1a9adcc8 -> 0x558e1aa63918]: print print.myclass print
#> [1] 1 2 3
Run Code Online (Sandbox Code Playgroud)

原件print不会创建以下副本x

x <- 1:3
x <- structure(x, class = "myclass")
tracemem(x)
#> [1] "<0x557799ebeb88>"

print(x)
#> [1] 1 2 3
#> attr(,"class")
#> [1] "myclass"
Run Code Online (Sandbox Code Playgroud)

Hadley 在他的《Advanced R》一书中建议,可以使用子集方法NextMethod()防止创建副本。我花了一些时间思考如何在打印具有属性的 S3 对象时防止创建副本,但我不知道如何做到这一点。

事实上,我刚刚测试了打印 atibble并且还创建了一个副本:

z <- tibble::tibble(x = 1, y = 2)
tracemem(z)
#> [1] "<0x55e2aa368c48>"

print(z)
#> tracemem[0x55e2aa368c48 -> 0x55e2aa339e48]: lapply tbl_subset_row [.tbl_df [ do.call head.data.frame head as.data.frame tbl_format_setup.tbl tbl_format_setup_ tbl_format_setup format.tbl format writeLines print.tbl
#> # A tibble: 1 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     2
Run Code Online (Sandbox Code Playgroud)

但 adata.table没有:

z <- data.table::data.table(x = 1, y = 2)
tracemem(z)
#> [1] "<0x55d64b2bd310>"

print(z)
#>    x y
#> 1: 1 2
Run Code Online (Sandbox Code Playgroud)

所以我想这个问题是可以解决的,但我还没弄清楚如何......

编辑:

好的,我做了一些阅读print.data.frame,它通过将 转换data.frame为 amatrix然后打印这个矩阵来解决这个问题。这是一个非常聪明的解决方案,我猜data.table使用了类似的东西。

然而,更一般地说,这对于某些其他类型的对象(例如,无法格式化为矩阵的对象)不起作用。拿这个:

z <- list(a = data.frame(a = 1, b = 2), b = data.frame(c = 3, d = 4))
z <- structure(z, class = "myotherclass")
tracemem(z)
#> [1] "<0x56298a95fd48>"

print(z)
#> $a
#>   a b
#> 1 1 2
#> 
#> $b
#>   c d
#> 1 3 4
#> 
#> attr(,"class")
#> [1] "myotherclass"

print.myotherclass <- function(x, ...) {
  print(unclass(x), ...)
  return(invisible(x))
}

print(z)
#> tracemem[0x56298a95fd48 -> 0x56298aaa58a8]: print print.myotherclass print
#> $a
#>   a b
#> 1 1 2
#> 
#> $b
#>   c d
#> 1 3 4
Run Code Online (Sandbox Code Playgroud)

在这种情况下你是怎么做的?

bob*_*bel 4

print(x[seq_along(x)])在你的打印方法中使用类似的东西怎么样?它避免了看起来的复制,并且属性仍然存在。

z <- list(a = data.frame(a = 1, b = 2), b = data.frame(c = 3, d = 4))
z <- structure(z, class = "myotherclass")
tracemem(z)

print.myotherclass <- function(x, ...) {
  print(x[seq_along(x)])
  return(invisible(x))
}

y <- print(z)
#> $`a`
#> a b
#> 1 1 2
#> 
#> $b
#> c d
#> 1 3 4

attributes(y)
#> $`names`
#> [1] "a" "b"
#> 
#> $class
#> [1] "myotherclass"
Run Code Online (Sandbox Code Playgroud)