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

Posted

技术标签:

【中文标题】通过 :: 调用包中的函数是一种好习惯吗【英文标题】:Is it a good practice to call functions in a package via :: 【发布时间】:2014-06-07 14:34:02 【问题描述】:

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

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

【问题讨论】:

require 通常用在包的函数中,this SO post 可以很好地区分它和library。如果您确定只需要一个包中的一个(或两个)函数,:: 很好,但我只在存在命名空间冲突时才会倾向于它。另外,不要忘记 ::: 运算符。 【参考方案1】:

这一切都取决于上下文。

:: 主要是在存在 命名空间冲突,来自不同包的同名函数。当我加载dplyr 包时,它提供了一个函数filter,它与stats 包中默认加载的filter 函数发生冲突(并屏蔽)。所以如果我想使用stats 版本的函数,我需要用stats::filter 调用它。这也为不加载大量包提供了动力。如果你真的只想要一个包中的一个功能,使用:: 比加载整个包更好,特别是如果你知道包会屏蔽你想使用的其他功能。

不是在代码中,而是在文本中,我发现:: 非常有用。键入stats::filter 比“stats 包中的filter 函数”要简洁得多。

性能的角度来看,使用:: 的价格(非常)小。长期 R-Core 开发团队成员 Martin Maechler 写道(r-devel mailing list (Sept 2017))

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

性能损失非常小,大​​约为几微秒,因此只有在需要高度优化的代码时才需要考虑。运行使用:: 的代码行一百万次将比不使用:: 的代码多花一两秒。

就可移植性而言,在脚本顶部显式加载包非常好,因为这样可以轻松浏览前几行并查看需要哪些包,如果需要,在深入了解任何内容之前安装它们否则,即在一个漫长的过程中进行到一半,而现在如果不重新开始就无法完成。

除此之外:可以提出类似的论点来选择library() 而不是require()。如果包不存在,库将导致错误并停止,而 require 将发出警告但继续。如果你的代码有一个应急计划以防包不存在,那么一定要使用if (require(package)) ...,但如果你的代码在没有包的情况下会失败,你应该在顶部使用library(package),这样它就会尽早明确地失败。

在您自己的包中

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

我个人的印象是 :: 是 现在很多“过度使用”,特别是在我强烈推荐的包装中 提倡在 NAMESPACE 中使用 importFrom(),所以这一切都会发生 在包加载时,然后 not 在包中使用 :: 来源。

另一方面,RStudio 首席科学家 Hadley Wickham 在他的 R Packages book 中说:

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

两位受人尊敬的 R 专家给出了相反的建议,我认为可以公平地说,您应该选择最适合您并满足您对清晰度、效率和可维护性的需求的风格。


如果您经常发现自己只使用另一个包中的一个函数,您可以复制代码并将其添加到您自己的包中。例如,我有一个个人使用的包,它从Hmisc 包中借用了%nin%,因为我认为这是一个很棒的功能,但我不经常使用来自Hmisc 的其他任何东西。使用roxygen2,可以很容易地添加@author@references 以正确地为借用函数分配代码。执行此操作时还要确保软件包许可证兼容。

【讨论】:

在脚本顶部使用library(...) 的另一个好处是,如果有人在未安装软件包的情况下尝试source 您的文件,source 命令将提前失败(而不是在可能很长时间之后)数据加载或操作)。 require 被编写为返回一个逻辑值,使其易于在构造中使用:if( !require(pkg) ) cat("informative error message") @BondedDust 在这种情况下,最好使用stopwarning 而不是cat 如果你正在编写一个包,我认为最好使用 :: 而不是命名空间导入或 importsFrom (除非你使用 lot 的函数其他包) @shujaa 是的,它应该在DESCRIPTION导入中,但它不应该在NAMESPACE import(pkg) 或 importFrom(pkg,foo) 中(我认为我原来的评论有点混乱)

以上是关于通过 :: 调用包中的函数是一种好习惯吗的主要内容,如果未能解决你的问题,请参考以下文章

在 std::thread 创建的线程中调用 pthread_sigmask 是一种好习惯吗?

使用 App 首字母缩写词为所有类/接口/函数添加前缀是一种好习惯吗?

将 if 语句放在 main() 或函数内部以响应用户输入是一种好习惯吗?

使用引用来布置简单的功能是一种好习惯吗

将函数放入 javascript 对象中是一种好习惯吗? [复制]

通过指向常量的指针释放内存是一种好习惯吗