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

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

最佳答案

这是一个单线做到这一点。这里 PackageFuncAstats::acfPackageFuncBstats:::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/

10-15 01:49