【问题标题】:Get the attribute of a packaged function from within itself从自身内部获取封装函数的属性
【发布时间】:2013-04-21 07:10:13
【问题描述】:

假设我们在 R 包中有这个函数。

prova <- function() {
  print(attr(prova, 'myattr'))
  print(myattr(prova))
  invisible(TRUE)
}
'myattr<-' <- function(x, value) {
  attr(x, 'myattr') <- value
  x
}
myattr <- function(x) attr(x, 'myattr')

所以,我安装了软件包,然后进行了测试。结果如下:

prova()
# NULL
# NULL
myattr(prova) <- 'ciao' # setting 'ciao' for 'myattr' attribute
prova()
# NULL
# NULL # Why NULL here ?
myattr(prova)
# [1] "ciao"
attr(prova, 'myattr')
# [1] "ciao"

问题是:如何从自身内部获取函数的属性?

在函数内部,我无法获取它的属性,如示例所示。

我认为解决方案将是“语言计算”系列(match.call()[[1L]]substitute、环境和朋友)。我错了吗?

我认为这里的重点是这个函数在一个包中(所以,它有它的环境和命名空间),我需要它自身的属性,在包裹里,不在外面。

【问题讨论】:

  • 这可能值得澄清:getting 属性和printing 属性是两个不同的东西。
  • @RicardoSaporta 我需要验证属性是否存在并获取其值。

标签: r namespaces package environment attr


【解决方案1】:

您可以将getenvir 参数一起使用。

prova <- function() {

  print(attr(get("prova", envir=envir.prova), 'myattr'))
  print(myattr(prova))
  invisible(TRUE)
}

例如:

envir.prova <- environment()
prova()
# NULL
# NULL
myattr(prova) <- 'ciao' 
prova()
# [1] "ciao"
# [1] "ciao" 

其中envir.prova 是一个变量,您将其值设置为定义prova 的环境。
或者,您可以使用get(.. envir=parent.frame()),但这不太可靠,因为您还必须跟踪调用,并确保目标环境和调用环境之间存在另一个同名对象。

关于 cmets 问题的更新:

关于使用parent.frame() 与使用显式环境名称:parent.frame,顾名思义,“上一层”。通常,这正是您想去的地方,所以效果很好。然而,即使您的目标是在更高的环境中获取对象,R 也会向上搜索调用堆栈,直到找到具有匹配名称的对象。所以很多时候,parent.frame() 就好了。

但是如果在您调用 parent.frame() 的位置和对象所在的位置之间有多个调用并且在其中一个中间环境中存在另一个对象相同的名称,则R 将在该中间环境停止并返回 对象,该对象不是您正在寻找的对象。

因此,parent.frame() 有一个参数 n(默认为 1),因此您可以告诉 Rn 级别后面开始搜索。

这就是我所指的“跟踪”,开发人员必须注意两者之间的调用次数。解决此问题的直接方法是在调用相关函数的每个函数中都有一个 n 参数,并将该值默认为 1。然后对于 envir 参数,您使用:get/assign/eval/etc @987654341 @

然后如果你从Func1 调用Func2,(Func1Func2 都有一个n 参数),并且Func2 正在调用prova,你使用:

  Func1 <- function(x, y, ..., n=1) { 

    ... some stuff ... 
    Func2( <some, parameters, etc,> n=n+1)
  }  


  Func2 <- function(a, b, c, ..., n=1) { 

     .... some stuff.... 
     eval(quote(prova()), envir=parent.frame(n=n) )
  }

如您所见,它并不复杂,但它*乏味*,有时看起来像是一个错误,这只是忘记携带 n。

因此,我更喜欢使用带有环境名称的固定变量。

【讨论】:

  • 我觉得我们都接近解决方案;),即使可能由于我的一些错误,您的解决方案对我不起作用。关于我的解决方案(现在我发布)我完全坚持这一点:那么你也必须跟踪电话并确保..事实上,我不希望用户必须这样做调用设置属性的函数的任何其他内容。
  • 我想我已经取得了一些进步,并且学到了一些新东西。顺便说一句,我更新了我的答案,使其更完整以供将来参考。
  • 但是,这可能是我的错,但我无法使用您的技术(即使用带有环境名称的固定变量)。明天我会再试一次。
  • @leodido,尝试使用全局分配(带双括号)&lt;&lt;- 而不是 &lt;- 即:envir.prova &lt;&lt;- environment()
  • 我认为您误诊了问题:当您执行myattr(prova) &lt;- 'ciao' 时,您正在全局环境中创建一个名为prova 的新对象,而不是修改名为prova 的对象的属性在你的包裹里。
【解决方案2】:

我找到的解决方案是:

myattr <- function(x) attr(x, 'myattr')

'myattr<-' <- function(x, value) {
  # check that x is a function (e.g. the prova function)
  # checks on value (e.g. also value is a function with a given precise signature)
  attr(x, 'myattr') <- value
  x
}

prova <- function(..., env = parent.frame()) {
  # get the current function object (in its environment)
  this <- eval(match.call()[[1L]], env)
  # print(eval(as.call(c(myattr, this)), env)) # alternative
  print(myattr(this))
  # print(attr(this, 'myattr')

  invisible(TRUE)
}   

我要感谢 @RicardoSaporta 提供的帮助和关于跟踪通话的澄清。

此解决方案在以下情况下不起作用myattr(prova) &lt;- function() TRUE 嵌套在 func1 中,而 provafunc2 中调用(它由 func1 调用)。除非你没有正确更新它的参数env ...

为了完整起见,根据@RicardoSaporta 的建议,我稍微修改了prova 函数:

prova <- function(..., pos = 1L) {
  # get the current function object (in its environment)
  this <- eval(match.call()[[1L]], parent.frame(n = pos)
  print(myattr(this))
  # ...
}  

这样,如果传入正确的pos 参数,它在嵌套时也可以工作。

通过此修改,可以更轻松地找出您在函数prova 上设置属性的环境。

myfun1 <- function() {
  myattr(prova) <- function() print(FALSE)
  myfun2(n = 2)
}
myfun2 <- function(n) {
  prova(pos = n)
}
myfun1()

# function() print(FALSE)
# <environment: 0x22e8208>

【讨论】:

  • 函数f 起作用的原因是myattr 是在其范围外部定义的。因此,当myattr 开始寻找prova 时,它会从之前 f 开始,因此永远不会看到prova 本地的prova 对象f。当您从f 内部调用prova() 时,( )R 知道寻找一个 函数,它在f 内部找不到该函数,因此会向上搜索调用堆栈。如果您将myattr&lt;- 的定义带到INSIDE f (将其放在您放置prova&lt;-'bye' 的上方,我怀疑您会看到不同的结果。
猜你喜欢
  • 1970-01-01
  • 2010-10-25
  • 2013-10-07
  • 2013-10-30
  • 1970-01-01
  • 2021-10-14
  • 2021-01-14
  • 1970-01-01
相关资源
最近更新 更多