2011-11-20 43 views
11

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

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

+0

创建自己的PackageFunA并更改对PackageFunB的调用以便它调用您的函数会更容易吗? – joran

+0

请参阅'?assignInNamespace' – Andrie

+0

joran - 我宁愿不维护自己的PackageFuncA版本,特别是因为它不仅仅是几行代码。 – SFun28

回答

10

这是一个单线程,它可以做到这一点。这里的PackageFuncAstats::acfPackageFuncB是我们想用my.plot.acf替换的stats:::plot.acfmy.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()的第一个参数是原始对象的父项。

在上面的例子中,它的设置使得acf变量引用插入到proto对象中的版本acf(除了原来的对象被修改为原始对象之外,它与原始对象相同)。当运行新的acf函数时plot.acf是一个自由变量(即,未在acf中定义),因此它在acf的父级中查找,并且它是原始对象中的环境,它在其中找到新的plot.acfacf可能有其他自由变量,但在这些情况下,因为它们在原始对象中未找到,它将查看原始对象的父级,原始对象是原始的acf的原始环境。在图方面,我们有这个地方<-意味着左侧是右侧的父:

environment(stats::acf) <- proto object <- revised acf 

与原对象既包含plot.acf和修订后的acf

我们还将新的plot.acf的环境设置为原始对象。我们可能需要也可能不需要这样做。在很多情况下,这并不重要。如果它是重要的是不要设置新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) 

在这种情况下,我们没有下过修订acfe作为原例如,但只设置其父。事实上,将修订的acf纳入e或原始对象并非绝对必要,但仅在原始案例中完成,因为原型具有重置环境的副作用,并且是我们之后的副作用。另一方面,有必要将修改后的plot.acf放在e或原始对象中,以便在原始对象之前遇到它。

您可能想要阅读此paper,特别是第21页开始的代理部分,因为此处显示的技术是代理对象的示例。

+0

完美的作品!在找出原因之前,我必须盯着这一点。如果您有足够的时间,对发生的事情进行简短的介绍会是一个巨大的帮助。 – SFun28

+0

好的。增加了一些细节。 –

+0

这太棒了!非常感谢 – SFun28

0

制作PackageFuncA的新副本,重置其环境并编写您自己的PackageFuncB版本。

environment(PackageFuncA) <- globalenv() # makes a new copy of PackageFuncA 

PackageFuncB <- function(...) .... # will be called from your new PackageFuncA 

您可能需要做一些编辑,如果PackageFuncA使用未导出的功能从原来的包。此外,如果您不希望在其他地方使用新的PackageFuncB,则可以将其包装在新的PackageFuncA中,而不是将其放置在全球环境中。