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

SFu*_*n28 11 r

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

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

G. *_*eck 10

这是一个单行班.这里PackageFuncAstats::acfPackageFuncBstats:::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)
Run Code Online (Sandbox Code Playgroud)

proto对象是一种环境,通过该proto函数插入到对象中的任何函数都会将其环境自动重置为该对象.第一个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
Run Code Online (Sandbox Code Playgroud)

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

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

acf <- with(p <- proto(environment(acf), acf = stats::acf), acf)
p[["plot.acf"]] <- my.plot.acf
Run Code Online (Sandbox Code Playgroud)

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

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

# 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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们没有像原始示例acfe那样放置修改后的内容,而只是设置其父级.事实上,配售修订acf进入e或原对象不是绝对必要的,但仅是在原的情况下完成,因为原具有复位环境的副作用,这是我们以后的副作用.另一方面,有必要将修订plot.acf后的e或原型对象放在原始对象之前.

您可能希望阅读本文,特别是关于代理开始第21页的部分,因为此处显示的技术是代理对象的示例.