假设我正在调用存在于第三方包(即来自CRAN的库)中的函数PackageFuncA。 PackageFuncA依次在同一个第三方包中调用PackageFuncB。有没有办法调用PackageFuncA,这样当它调用PackageFuncB时,它实际上会调用我自己的PackageFuncB?换句话说,我可以拦截对PackageFuncB的调用吗?
我认为解决方案涉及创建我自己的PackageFuncB函数,然后在同一环境中调用PackageFuncA而不是PackageFuncA的环境,但我无法使用do.call或eval。
答案 0 :(得分:10)
这是一个单行班。此处PackageFuncA
为stats::acf
,PackageFuncB
为stats:::plot.acf
,我们希望将其替换为my.plot.acf
。 my.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.acf
。 acf
可能有其他自由变量,但在这些情况下,因为它们未在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
中,而不是将其放在全局环境中。