假设我正在调用存在于第三方包(即来自CRAN的库)中的函数PackageFuncA。 PackageFuncA依次在同一个第三方包中调用PackageFuncB。有没有办法调用PackageFuncA,这样当它调用PackageFuncB时,它实际上会调用我自己的PackageFuncB实现?换句话说,我可以拦截对PackageFuncB的调用吗?
我认为解决方案涉及创建我自己的PackageFuncB函数,然后在同一环境中调用PackageFuncA而不是PackageFuncA的环境,但我无法使用do.call或eval。
这是一个单行班。这里 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
function将其环境自动重置为该对象。第一个arg proto()
是proto对象的父级。
在上面的例子中,它已被设置为 acf
变量是指版本的 acf
插入proto对象(与原始对象相同,除了其环境已被修改为proto对象)。当新的 acf
功能运行 plot.acf
是一个自由变量(即未定义) acf
所以它被抬起来了 acf
的父级,这是原型对象中找到新的环境 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案例中完成,因为proto具有重置环境的副作用,而这正是我们追求的副作用。另一方面,有必要修改 plot.acf
在 e
或原始对象,以便在原始对象之前遇到它。
你可能想读这个 纸 并且,特别是关于代理开始第21页的部分,因为此处显示的技术是代理对象的示例。
制作新的副本 PackageFuncA
,重置其环境,并编写自己的PackageFuncB版本。
environment(PackageFuncA) <- globalenv() # makes a new copy of PackageFuncA
PackageFuncB <- function(...) .... # will be called from your new PackageFuncA
你可能需要做一些编辑 PackageFuncA
使用原始包中的未导出函数。此外,如果你不想要新的 PackageFuncB
要在其他地方使用,你可以将它包装在新的 PackageFuncA
而不是将其置于全球环境中。