r - 是否可以通过不同的包“反转”功能屏蔽?
问题描述
我维护一个包,供我工作的公司使用。并不是所有的程序员都像他们应该做的那样勤奋,而且他们经常library()
为他们需要的每个包发送垃圾电话。这通常会导致一个包中的函数被稍后加载的另一个包屏蔽,从而导致代码中其他地方的复杂性和奇怪的错误。然后通过使用package::function
语法来解决这些错误,当它用于一个包函数时看起来很奇怪,但它周围没有其他任何东西,即使来自同一包中的其他函数(未屏蔽)也是如此。
显然,在这种情况下,正确的做法是不要使用 加载每个单独的包library()
,将其保存为代码中最常用的包,然后使用package::function
不常用的包的语法。
然而,这超出了我的控制范围。所以我试图通过向我们的内部包添加一个处理包“优先级”的函数来帮助程序员,让他们定义在调用给定函数名时哪个包应该优先。
以下代码有效:
setDefaultPackage <- function(pkg, functions = NULL) {
pkg = paste0("package:", pkg)
if (is.null(functions)) functions <- utils::lsf.str(pkg)
for(f in functions) {
# only reassign if name doesn't yet exist or if associated environment is
# NOT the global environment.
if (exists(f)) {
canUnmask <- tryCatch({
getNamespaceName(environment(get(f, pos = parent.frame())))
TRUE
}, error = function (e) {
FALSE
})
} else {
canUnmask <- TRUE
}
if (canUnmask) {
x <- tryCatch({
get(pos = pkg, f)
}, error = function(e) {
stop("Package ", pkg, "does not have a function called ", f)
})
assign(f, x, pos = parent.frame())
}
}
}
library(stats)
environmentName(environment(filter))
#> [1] "stats"
# now mask stats::filter with dplyr::filter
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
environmentName(environment(filter))
#> [1] "dplyr"
# restore stats::filter to the top
setDefaultPackage("stats")
environmentName(environment(filter))
#> [1] "stats"
# restore dplyr::filter to the top
setDefaultPackage("dplyr")
environmentName(environment(filter))
#> [1] "dplyr"
# Fails to unmask packaged names masked by local names
filter <- function() {print(1)}
environmentName(environment(filter))
#> [1] "R_GlobalEnv"
setDefaultPackage("stats")
environmentName(environment(filter))
#> [1] "R_GlobalEnv" -- (unchanged!) --
由reprex 包于 2021-08-11 创建 (v2.0.0 )
用户可以定义他们是否要filter
调用stats::filter
或dplyr::filter
。
然而,这个函数非常不优雅:它通过在本地框架中定义函数名称(在上面的示例中,全局框架;如果在另一个函数中调用,调用函数的框架)来工作,淹没该框架的名称空间。
在这种情况下,命名空间中充斥着来自stats
和的对象名称dplyr
,因为我已经调用setDefaultPackage
了两者(冲突的名称当前指向,stats
因为那是我最后一次调用)。
一种更简洁的方法是简单地修改搜索路径:
search()
#> [1] ".GlobalEnv" "package:dplyr" "tools:rstudio" "package:stats" "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#> [9] "package:methods" "Autoloads" "package:base"
由于我加载dplyr
后stats
,它在搜索列表中排在第一位。
如果我可以简单地修改该列表,改组package:dplyr
和package:stats
左右,那就太棒了。那可能吗?
也就是说,有没有办法将特定的包放在搜索列表的顶部?
someMagicalFunction("stats")
search()
#> [1] ".GlobalEnv" "package:stats" "package:dplyr" "tools:rstudio" "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#> [9] "package:methods" "Autoloads" "package:base"
解决方案
您可以使用以下自定义功能:
setDefaultPackage <- function(pkg, functions = NULL){
pkg1 <- paste0("package:", pkg)
nms <- paste(pkg, 'functions', sep = '_')
if (is.null(functions)) {
if (any(search() == pkg1))
detach(pkg1, character.only = TRUE)
library(pkg, character.only = TRUE)
}
else {
if (any(search() == nms))
detach(nms, character.only = TRUE)
env <- list2env(mget(functions, as.environment(pkg1)))
attach(env, name = nms)
}
}
例子:
> library(dplyr)
> search()
#> [1] ".GlobalEnv" "package:dplyr" "tools:rstudio" "package:stats"
#> [5] "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#> [9] "package:methods" "Autoloads" "package:base"
现在,如果您想优先考虑stats
包中的所有功能:
> setDefaultPackage('stats')
#> Attaching package: ‘stats’
#> The following objects are masked from ‘package:dplyr’:
#> filter, lag
> search()
#> [1] ".GlobalEnv" "package:stats" "package:dplyr" "tools:rstudio"
#> [5] "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#> [9] "package:methods" "Autoloads" "package:base"
我们看到stats
package 出现在dplyr
package 之前,意味着我们可以使用lag
和filter
from stats
package。
让我们将其还原为dplry
之前的内容stats
:
> setDefaultPackage('dplyr')
> search()
#> [1] ".GlobalEnv" "package:dplyr" "tools:rstudio" "package:stats"
#> [5] "package:graphics" "package:grDevices" "package:utils" "package:datasets"
#> [9] "package:methods" "Autoloads" "package:base"
如果从一开始,我们想使用lag
fromdplyr
但filter
from怎么办stats
?即在搜索路径中,dplyr 出现在 stats 之前,就像以前一样。然后你可以运行
setDefaultPackage('stats', 'filter')
现在filter
要使用的是 from stats
,whilelag
是 from dplyr
。
推荐阅读
- credentials - 有没有办法使用不同的交换帐户在蓝色棱镜中发送电子邮件?
- matlab - 从 fdatool IIR 滤波器对象中提取滤波器系数
- amazon-web-services - 你能在“Fn::Sub”中嵌套“Fn::FindInMap”吗?
- python - 转义包含降价 URL 的字符串中的下划线字符
- javascript - 如何计算点击可拖动画布元素的位置?
- r - 计算数据框中因子变量的数量
- json - 从 JSON 响应中检索值,并创建一个下拉列表
- reactjs - 确认远程同步 - 带有 ReactJS+Redux+Saga 的 Firebase 实时数据库
- python - 列出人名
- android - Flutter aduioplayers MissingPluginException(在通道 xyz.luan/audioplayers 上找不到方法 startHeadlessService 的实现)