R:从包中覆盖函数的正确方法是什么?

时间:2014-10-30 03:41:53

标签: r namespaces package assign

我正在使用R包,其中有2个函数f1和f2(f2调用f1)
我希望覆盖函数f1。

由于R 2.15和包中命名空间的强制使用,如果我只是获取新函数,它确实可以在全局环境中使用(即在控制台中只调用f1(x)返回新结果)。但是,调用f2仍将使用打包的函数f1。 (因为命名空间修改了搜索路径,并按照here in the Writing R Extensions教程所解释的那样密封它) 用新的方法完全替换f1的正确方法是什么? (除了再次构建包装!)这在几种情况下都很有用。例如,如果包中存在您尚未开发的错误。或者,如果您不想在每天仍在开发过程中重新构建软件包。

我知道功能

assignInNamespace("f1",f1,ns="mypackage")

然而,帮助页面?assignInNamespace有点令人讨厌,似乎不鼓励人们在不提供更多信息的情况下使用它,而且我无法在官方CRAN教程中找到任何最佳实践建议。并在调用此函数后:

# Any of these 2 calls return the new function
mypackage::f1 
getFromNamespace(x = "f1", envir = as.environment("package:mypackage"))

# while this one still returns the old packaged version
getFunction(name = "f1", where = as.environment("package:mypackage")) 

这非常令人不安。搜索路径如何受到影响?

现在我正在做一些丑陋的事情,例如修改lockEnvironment函数,以便library不会锁定包名称空间,我可以在以后锁定它取代f1(这似乎不是一个好习惯)

基本上我有两个问题:

  1. 在包名称空间(应该被锁定)的情况下,assignInNamespace到底做了什么
  2. 有什么好的做法?
  3. 非常感谢您在那里分享您的经验。

    编辑:对这个问题感兴趣的人可能会发现this blog post非常有趣。

2 个答案:

答案 0 :(得分:6)

这里有很多不同的案例。

如果这是其他人包裹中的错误
然后最好的做法是联系包维护者并说服他们修复它。这样每个人都可以得到修复,而不仅仅是你。

如果在开发自己的软件包时出现错误
然后,您需要找到一个易于重建包的工作流程。就像使用devtools包并输入build(mypackage),或点击按钮(RStudio中的“Build& Reload”;建筑师中的“R CMD build”)。

如果您只想要对现有软件包采取不同的行为
如果它不是这样的bug,或者包维护者不能进行你想要的修复,那么你将必须维护自己的f1副本。使用assignInNamespace在现有包中覆盖它可以进行探索,但它有点笨拙,所以它不适合永久解决方案。

您最好的办法是创建自己的包,其中包含f1f2的副本。这比听起来要省力,因为你可以定义f2 <- existingpackage::f2


回应评论:

  

如果你是独自一人,那么第二和第三种情况是有意义的,但是他们需要构建和安装在我的组织中很棘手的软件包,因为软件包部署在几十台计算机上,我需要root访问权来更新软件包。

因此,请获取现有软件包源的副本,应用您的修补程序,并将其托管在您的公司网络或github或Bitbucket上。然后可以通过

以编程方式安装更新的软件包
install.packages("//some/network/path/mypackage_0.0-1.tar.gz", repos = NULL)

library(devtools)
install_github("mypackage", "mygithubusername")

由于安装只是一行代码,因此您可以轻松地将其推送到任意数量的计算机。您也不需要root访问权限 - 只需将软件包安装到不需要root访问权限即可写入的库文件夹中。 (请阅读Startup.libPaths帮助页面,了解如何定义新库。)您需要对这些计算机进行网络访问,但我无法帮助您。与您的网络管理员或您的老板或任何可以获得您许可的人交谈。

答案 1 :(得分:1)

如果函数在包中没有显式绑定:

rlang::env_unlock(env = asNamespace('mypackage'))
rlang::env_binding_unlock(env = asNamespace('mypackage'))
assign('f1', f1, envir = asNamespace('mypackage'))
rlang::env_binding_lock(env = asNamespace('mypackage'))
rlang::env_lock(asNamespace('mypackage'))