覆盖在命名空间中导入的函数

Posted

技术标签:

【中文标题】覆盖在命名空间中导入的函数【英文标题】:Override a function that is imported in a namespace 【发布时间】:2011-09-09 10:20:02 【问题描述】:

由于 R 中的 termplot 函数包含一些奇怪的代码,这些代码会给我带来烦人的错误,我想在我自己的测试代码中覆盖它,直到找到更持久的解决方案。问题是 mgcv 包没有加载更改的函数。 mgcv 包使用 NAMESPACE 文件中的 importFrom() 从其命名空间中的 stats 包加载 termplot。

如何说服 mgcv 使用更改后的术语图?我试过了:

unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, as.environment("package:stats"))
lockBinding("termplot", as.environment("package:stats"))

当应用于 lm-objects 时,这有效并且使用了更改后的术语图。但是当使用由 mgcv 包制作的 gam-objects 时,这是行不通的。如果可以避免的话,我真的不会从源代码构建 stats 包...

为了澄清,我也尝试过

assignInNamespace("termplot", my.termplot, ns="stats")
assignInNamespace("termplot", my.termplot, ns="mgcv")

在所有可能的组合中,在附加 mgcv 之前,在附加 mgcv 之后,我没有设法让它工作。


编辑:

我尝试了这里给出的所有选项(除了重建任何一个包),但无法让它工作。对我来说最简单的方法是使用包装函数。可以在here 找到该讨论。感谢所有提示。


一个可重现的例子:

my.termplot <- function (x) print("my new termplot")

  unlockBinding("termplot", as.environment("package:stats"))
  assignInNamespace("termplot", my.termplot, ns="stats", envir=as.environment("package:stats"))
  assign("termplot", my.termplot, as.environment("package:stats"))
  lockBinding("termplot", as.environment("package:stats"))


y <- 1:10
x <- 1:10
xx <- lm(y~x)
termplot(xx)
require(mgcv)
dat <- gamSim(1, n = 400, dist = "normal", scale = 2)
b <- gam(y ~ s(x0) + s(x1) + s(x2) + x3, data = dat)
plot(b,all=TRUE)

plot.gam 为非平滑项(在本例中为 x3)调用 termplot,但找不到新的 termplot 函数。


EDIT2:显然,我的示例有效。我现在看到我解决了自己的问题:在第一个代码中,我没有在 assignInNamespace 中添加命名空间和包。重要的是要记住在加载另一个包之前更改命名空间和包中的函数。感谢@hadley 为我指明了正确的方向,@Marek 用于测试代码并报告它的工作原理,其余的则用于努力回答。

【问题讨论】:

您在加载统计信息之前尝试使用assignInNamespace 吗?例如。在.Rprofile中? ?assignInNamespace 中有注释:“assignInNamespace 更改命名空间中的副本,但没有任何已从命名空间导出的副本” @Joris:请举例说明您如何调用termplot。显而易见的答案是打电话给my.termplot 或你覆盖的stats::termplot,但我想这对你来说是不可能的。 @Richie @Joris 并没有真正调用 termplot(),mgcv 正在调用 termplot(),而 Joris 想要一种方法让 mgcv 看到他分配给 stats 命名空间的新版本 termplot ,但无法让 mgcv 看到除原件之外的任何内容。 @Marek :会尝试。我确实检查过; termplotstats:termplot 都返回正确的函数。这就是我使用整个 unlockBinding... 的原因。但不知何故,mgcv 在其他地方得到了它。谢谢你的小费 这将很难,因为您需要覆盖 S3 方法表中的函数。 【参考方案1】:

我很难过 - 我无法弄清楚 plot.gam 是如何定位 termplot - 据我所知,它没有使用普通的范围规则。这似乎需要对命名空间的理解比我目前拥有的更深入。

my.termplot <- function (x) print("my new termplot")

# where is it defined?
getAnywhere("termplot")
# in package and in namespace

unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, "package:stats")

unlockBinding("termplot", getNamespace("stats"))
assign("termplot", my.termplot, getNamespace("stats"))

getAnywhere("termplot")[1]
getAnywhere("termplot")[2]
# now changed in both places

y <- 1:10
x <- 1:10 + runif(10)
xx <- lm(y ~ x)
termplot(xx) # works

library("mgcv")
b <- gam(y ~ s(x), data = data.frame(x, y))
plot(b) # still calls the old termplot

# I'm mystified - if try and find termplot as
# seen from the environment of plot.gam, it looks 
# like what we want
get("termplot", environment(plot.gam)) 

【讨论】:

我在debug 模式下运行plot.gam,它似乎从不调用termplottermplot 取决于if (n.para &gt; 0) 条件,在您的情况下为FALSEtermplot(b) 按预期工作。 但是在 Joris 数据上的工作就像一个魅力(即:返回关于未使用参数的错误)。 平滑器的绘图在函数 plot.gam() 中编码。仅当我是对的时才对非平滑项调用 Termplot,这就是我使用该示例的原因。您的代码确实有效。显然,我还需要在命名空间中解锁术语图。我猜... 哦,我以为我也尝试了原始示例,但都没有成功。 你怎么能阻止mgcv这样加载? - - 我不明白termplot 是如何覆盖所有其他plot 函数的。如何使库及其功能持久化?【参考方案2】:

我认为 trace() 函数会自动执行上面尝试的操作。做:

trace('termplot', edit='gedit')

其中 'gedit' 是文本编辑器的名称。编辑器将打开原始代码,您可以粘贴您想要的任何替换代码。

要返回原始版本,只需 untrace('termplot')

警告:当文本编辑器打开许多文件但它不起作用时,我尝试使用它。所以我在我的系统上使用了一个我不经常使用的文本编辑器“gedit”。这样我确信 R 会打开一个新的 'gedit' 实例。

我不确定这会有所帮助,但我认为值得一试。有命名空间时的搜索顺序真的很混乱。

【讨论】:

感谢您的提示,但这无济于事。我确实在 stats 包中获得了已编辑的版本,但 mgcv 继续使用旧版本。【参考方案3】:

尝试覆盖您从中调用termplot 的函数。猜测一下,这是mgcv 包中的plot.gam

首先加载必要的包。

library(mgcv)

这是您的备用 termplot 函数,已添加到 stats 命名空间。

my.termplot <- function (model, ...) 

  message("In my.termplot")


unlockBinding("termplot", as.environment("package:stats"))
assign("termplot", my.termplot, as.environment("package:stats"))
lockBinding("termplot", as.environment("package:stats"))

同样,这是添加到 mgcv 命名空间的包装函数。

my.plot.gam <- function (x, ...) 

  message("In my.plot.gam")
  my.termplot()


unlockBinding("plot.gam", as.environment("package:mgcv"))
assign("plot.gam", my.plot.gam, as.environment("package:mgcv"))
lockBinding("plot.gam", as.environment("package:mgcv"))

这是一个测试它的例子,取自?gam

dat <- gamSim(1, n = 400, dist = "normal", scale = 2)
b <- gam(y ~ s(x0) + s(x1) + s(x2) + s(x3), data = dat)
plot(b) 

【讨论】:

我必须同时覆盖 termplot 和 plot.gam,这会导致 mgcv 命名空间内出现新的冲突。试过了,我得到的只是更多的错误...... 使用示例代码,它可以工作。当我使用真实代码(即在 plot.gam 的情况下,正是 plot.gam 函数的代码)执行此操作时,我会从命名空间中的其他冲突中得到后续错误。不过感谢您的努力。

以上是关于覆盖在命名空间中导入的函数的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中覆盖命名空间

PHP命名空间

php命名空间

PHP 命名空间(namespace)

18PHP 命名空间(namespace)

如何使用来自其他命名空间的对象以及如何在 PHP 中导入命名空间