首页 > 解决方案 > 直接调用对象时获取传递给`print`的对象名称(不表示`print`函数)

问题描述

我正在尝试为我的新对象定义打印方法,并使用传递给printusing的对象名称deparse(substitute(y))。显式使用该print函数可以完美地工作:

obj <- structure(list(x = 1), 
                 class = "new_obj")

print.new_obj <- function(y){
  cat("New object name:\n")
  print(deparse(substitute(y)))
}

print(obj)
# New object name:
#   [1] "obj"

但是,当对象自己按名称调用时,生成的print函数不会检测到名称:

obj
# New object name:
#   [1] "x"

是否有一种标准方法可以在单独print传递对象名称时更改隐式调用的行为?

编辑:已将函数参数更改y为表示正在传递的对象,以证明无论第二次调用如何都返回“x”。

标签: r

解决方案


解释发生了什么比修复它更容易。如果我们从查看泛型开始,print我们可以看到它只是print通过以下方式调度适合类的方法UseMethod("print")

print
#> function (x, ...) 
#> UseMethod("print")

因此,当您调用时print(obj),您首先调用通用函数print(obj),然后调用print.new_obj(obj). print(sys.calls())我们可以通过添加到您的打印方法来确认这一点:

print.new_obj <- function(y){
  print(sys.calls())
  cat("New object name:\n")
  cat(deparse(substitute(y)))
}

print(obj)
#> [[1]]
#> print(obj)
#> 
#> [[2]]
#> print.new_obj(obj)
#> 
#> New object name:
#> obj

到目前为止,一切都很好,我怀疑你已经知道了这一切。

现在,当您obj在控制台中输入时会发生什么?

obj
#> [[1]]
#> (function (x, ...) 
#> UseMethod("print"))(x)
#> 
#> [[2]]
#> print.new_obj(x)
#> 
#> New object name:
#> x

现在我们可以看到它的x来源。它取自对泛型的幕后调用,泛型print实际上被称为未命名函数。因此,变量的名称实际上并不包含在调用堆栈中。SO还有其他问题,它说这使问题无法解决。这不是真的;这只是意味着您需要在调用堆栈之外查看您的对象:

print.new_obj <- function(y){
  obj_name <- deparse(substitute(x, parent.frame()))
  if (obj_name != "x")
  {
    obj_name <- names(which(sapply(ls(envir = parent.frame(2)), function(v) 
      identical(y, get(v, envir = parent.frame(2))))))[1]
    cat("New object name:\n", obj_name)
  }
  else cat("New object name:\n", deparse(substitute(y)))
}

print(obj)
#> New object name:
#>  obj
obj
#> New object name:
#>  obj

当然,出于各种原因,您不希望在生产代码中使用它。对于数据结构来说,知道它在特定环境中被分配了什么名称并不是特别有用或合乎逻辑的,也不是为其他用户编写包的惯用方式。

不过,很高兴知道这可能的。


推荐阅读