假设我正在调用存在于 3rd 方包(即来自 CRAN 的库)中的函数 PackageFuncA。 PackageFuncA 又在同一个第 3 方包中调用 PackageFuncB。有没有办法调用PackageFuncA,这样当它调用PackageFuncB 时,它实际上会调用我自己对PackageFuncB 的实现?换句话说,我可以拦截对 PackageFuncB 的调用吗?
我认为该解决方案涉及创建我自己的 PackageFuncB 函数,然后在同一环境中调用 PackageFuncA 而不是 PackageFuncA 的环境,但我无法让它与 do.call 或 eval 一起使用。
最佳答案
这是一个单线做到这一点。这里 PackageFuncA
是 stats::acf
和 PackageFuncB
是 stats:::plot.acf
我们要用 0x2518122313431 替换它。 my.plot.acf
打印 my.plot.acf
然后调用真正的 "Hello"
。
# 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)
原型(prototype)对象是一个环境,通过
stats:::plot.acf
函数插入对象的任何函数都会自动将其环境重置为该对象。 proto
的第一个 arg 是原型(prototype)对象的父对象。在上面的例子中,它被设置为
proto()
变量指的是插入到 proto 对象中的 acf
的版本(除了它的环境被修改为 proto 对象之外,它与原始对象相同)。当运行新acf
功能acf
是(即不在plot.acf
定义),所以它在acf
抬头的父自由变量,这就是在它找到的新acf
原癌对象的环境。 plot.acf
可能有其他自由变量,但在这些情况下,因为它们在 proto 对象中找不到,它会查看 proto 对象的父对象,该父对象是原始 acf
的原始环境。就图表而言,我们有这个,其中 acf
表示左侧是右侧的父级:environment(stats::acf) <- proto object <- revised acf
原型(prototype)对象包含
<-
和修改后的 plot.acf
。我们还将新的
acf
的环境设置为 proto 对象。我们可能需要也可能不需要这样做。在许多情况下,这无关紧要。如果不设置新 plot.acf
的环境很重要,那么就可以这样做,因为 proto 从未设置使用 plot.acf
插入的函数的环境: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)
在这种情况下,我们没有像原型(prototype)示例中那样将修改后的
[[...]]
放在 acf
中,而只是设置了它的父级。实际上,将修改后的 e
放入 acf
或 proto 对象并不是绝对必要的,而是仅在 proto 情况下完成的,因为 proto 具有重置环境的副作用,而这正是我们所追求的副作用。另一方面,有必要将修改后的 e
放在 plot.acf
或 proto 对象中,以便在原始对象之前遇到它。您可能想阅读这个 paper,特别是从第 21 页开始的代理部分,因为这里显示的技术是代理对象的一个例子。
关于包函数内的重定向/拦截函数调用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8204008/