包函数内的重定向/截取函数调用

时间:2011-11-20 19:20:52

标签: r

假设我正在调用存在于第三方包(即来自CRAN的库)中的函数PackageFuncA。 PackageFuncA依次在同一个第三方包中调用PackageFuncB。有没有办法调用PackageFuncA,这样当它调用PackageFuncB时,它实际上会调用我自己的PackageFuncB?换句话说,我可以拦截对PackageFuncB的调用吗?

我认为解决方案涉及创建我自己的PackageFuncB函数,然后在同一环境中调用PackageFuncA而不是PackageFuncA的环境,但我无法使用do.call或eval。

2 个答案:

答案 0 :(得分:10)

这是一个单行班。此处PackageFuncAstats::acfPackageFuncBstats:::plot.acf,我们希望将其替换为my.plot.acfmy.plot.acf打印"Hello",然后调用真实stats:::plot.acf

# we want this to run in place of stats:::plot.acf
my.plot.acf <- function(x, ...) { cat("Hello\n"); stats:::plot.acf(x, ...) }

# this does it
library(proto)
acf <- with(proto(environment(acf), acf = stats::acf, plot.acf = my.plot.acf), acf)

# test
acf(1:10)

proto对象是一种环境,通过proto函数插入到对象中的任何函数都会将其环境自动重置为该对象。 proto()的第一个arg是proto对象的父级。

在上面的示例中,已设置它以便acf变量引用插入到proto对象中的acf版本(除了其环境之外与原始对象相同)被修改为原型对象)。当新的acf函数运行时plot.acf是一个自由变量(即未在acf中定义),因此在acf的父级中查找它,这就是环境在proto对象中找到新的plot.acfacf可能有其他自由变量,但在这些情况下,因为它们未在proto对象中找到,它会查看proto对象的父对象,它是原始acf的原始环境。在图表方面,我们有<-表示左侧是右侧的父级:

environment(stats::acf) <- proto object <- revised acf

并且proto对象包含plot.acf和修订后的acf

我们还将新plot.acf的环境设置为proto对象。我们可能需要也可能不需要这样做。在许多情况下,这无关紧要。如果重要的是不设置新plot.acf的环境,那么就会这样做,因为proto永远不会设置使用[[...]]插入的函数的环境:

acf <- with(p <- proto(environment(acf), acf = stats::acf), acf)
p[["plot.acf"]] <- my.plot.acf

在这个例子中,两种方法都有效。

可以在普通环境中完成所有这些操作,但必须使用多行代码:

# create new environment whose parent is the original acf's parent
e <- new.env(parent = environment(stats::acf))

# the next statement is only need to overwrite any acf you already might have from
# trying other code.  If you were sure there was no revised acf already defined 
# then the next line could be omitted.  Its a bit safer to include it.
acf <- stats::acf

# This sets the environment of the new acf.  If there were no acf already here 
# then it would copy it from stats::acf .
environment(acf) <- e

# may or may not need next statement.  In this case it doesn't matter.
environment(my.plot.acf) <- e

e$plot.acf <- my.plot.acf

acf(1:10)

在这种情况下,我们没有在acf中将修订后的e放置在原型示例中,而只是设置其父级。事实上,将修订后的acf放入e或proto对象并不是绝对必要的,但只是在原型案例中完成,因为proto具有重置环境的副作用,这就是我们的副作用之后。另一方面,有必要将修订后的plot.acf放在e或proto对象中,以便在原始对象之前遇到它。

您可能希望阅读此paper,特别是第21页上的代理部分,因为此处显示的技术是代理对象的示例。

答案 1 :(得分:0)

制作PackageFuncA的新副本,重置其环境,并编写自己的PackageFuncB版本。

environment(PackageFuncA) <- globalenv()  # makes a new copy of PackageFuncA

PackageFuncB <- function(...) ....   # will be called from your new PackageFuncA

如果PackageFuncA使用原始包中未导出的函数,则可能需要进行一些编辑。此外,如果您不希望在其他地方使用新的PackageFuncB,则可以将其包装在新的PackageFuncA中,而不是将其放在全局环境中。