通过::调用包中的函数是一个好习惯吗?

时间:2014-04-23 00:38:20

标签: r scope namespaces scoping qualified-name

我正在编写一些R函数,这些函数在stringrbase64enc等其他包中使用了一些有用的函数。是不是先调用library(...)require(...)来加载这些包,而是使用::直接引用我需要的函数,例如stringr::str_match(...)

一般情况下这是一个好习惯吗?或者它会引起什么问题?

1 个答案:

答案 0 :(得分:45)

这一切都取决于背景。

如果存在命名空间冲突,则

::主要是必需的,来自具有相同名称的不同包的函数。当我加载dplyr包时,它提供了一个函数filter,它与filter包中默认加载的stats函数冲突(并屏蔽)。因此,如果我想使用该功能的stats版本,我需要使用stats::filter来调用它。这也为不加载大量软件包提供了动力。如果你真的只想要一个包中的一个函数,最好使用::而不是加载整个包,特别是如果你知道包会掩盖你想要使用的其他函数。

不在代码中,但在文本中,我发现::非常有用。键入stats::filter而不是" filter包中的stats函数"更为简洁。

效果的角度来看,使用::的价格非常低。 Martin Maechler写道(r-devel mailing list (Sept 2017)

  

许多人似乎忘记了::的每次使用都是R   与仅使用相比,函数调用和使用它是低效的   已导入的名称。

性能损失非常小,大​​约几微秒,所以当您需要高度优化的代码时,它只是一个问题。运行使用::一百万次的代码行将比不使用::的代码长一两秒。

就可移植性而言,在脚本顶部显式加载包是很好的,因为它可以轻松浏览前几行并查看需要的包,必要时安装它们。其他任何事情都太深了,也就是说,在没有重新开始的情况下完成一个现在无法完成的漫长过程的中途。

除了:可以使用类似的参数来优先library()而不是require()。如果包不在那里,库将导致错误并停止,而require将发出警告但仍继续。如果您的代码有一个应急计划,以防包裹不在那里,那么请务必使用if (require(package)) ...,但如果您的代码在没有包裹的情况下失败,您应该在顶部使用library(package)它很早就清楚地失败了。 (感谢Hugh和BondedDust的评论。)

编写自己的包

一般的解决方案是创建自己的包,imports您需要在DESCRIPTION文件中使用的其他包。安装软件包时将自动安装这些软件包,因此您可以在内部使用pkg::fun。或者,通过在NAMESPACE文件中导入它们,您可以import整个包或选择性importFrom特定功能,而不需要::。意见不同。 R-Core成员Martin Maechler(与上述相同的r-devel来源)说:

  

就个人而言,我的印象是:是   很多"过度使用"如今,特别是在我非常强烈的包装中   主张在NAMESPACE中使用importFrom(),所以这一切都发生了   在程序包加载时,然后使用程序包中的::   消息来源。

另一方面,知名包装开发商Hadley Wickham在R Packages book中说:

  

通常,Imports中的DESCRIPTION列出了包,但NAMESPACE中却没有。实际上,这就是我的建议:在DESCRIPTION中列出包以便安装它,然后始终使用pkg::fun()显式引用它。除非有充分的理由不这样做,否则最好是明确的。

有两位受人尊敬的R专家提出相反的建议,我认为你应该选择最适合你的风格并满足你对清晰度,效率和可维护性的需求。


如果您经常发现自己只使用其他软件包中的一个函数,则可以复制代码并将其添加到您自己的软件包中。例如,我有一个供个人使用的软件包,可以从%nin%软件包借用Hmisc,因为我认为这是一个很棒的功能,但我不经常使用{{1}中的任何其他内容。 1}}。使用Hmisc,可以轻松添加roxygen2@author以正确归属借用函数的代码。此外,还要确保包许可证兼容。