2011-11-20 73 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中,而不是將其放置在全球環境中。