R中包中的全局变量
我正在用R开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量R中包中的全局变量,r,global-variables,R,Global Variables,我正在用R开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量 我读过一些关于环境的书,但我不明白它是如何工作的,甚至不知道这是否是解决问题的方法。一般来说,全局变量是邪恶的。它们之所以邪恶的根本原则是,您希望最小化包中的互连。这些相互关联通常会导致函数产生副作用,即它不仅取决于输入参数以及结果,还取决于某些全局变量的值。特别是当函数的数量增加时,这可能很难正确执行,而且很难调试 对于R中的全局变量,请参见此 根据您的评论进行编辑: 另一种方法是将所需信息传递给需要它的函数
我读过一些关于环境的书,但我不明白它是如何工作的,甚至不知道这是否是解决问题的方法。一般来说,全局变量是邪恶的。它们之所以邪恶的根本原则是,您希望最小化包中的互连。这些相互关联通常会导致函数产生副作用,即它不仅取决于输入参数以及结果,还取决于某些全局变量的值。特别是当函数的数量增加时,这可能很难正确执行,而且很难调试 对于R中的全局变量,请参见此 根据您的评论进行编辑: 另一种方法是将所需信息传递给需要它的函数。您可以创建包含以下信息的新对象:
token_information = list(token1 = "087091287129387",
token2 = "UA2329723")
并要求所有需要此信息的函数将其作为参数:
do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)
通过这种方式,从代码中可以清楚地看出,函数中需要令牌信息,并且您可以自己调试函数。此外,函数没有副作用,因为它的行为完全由输入参数决定。典型的用户脚本如下所示:
token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)
我希望这能让事情变得更清楚。你可以设置一个
选项,例如
options("mypkg-myval"=3)
1+getOption("mypkg-myval")
[1] 4
问题不清楚:
- 只是一个R进程还是几个
- 仅在一台主机上,还是跨多台计算机
- 它们之间是否有共同的文件访问权限
随着复杂性的增加,我会使用一个文件,通过包或(我最喜欢的:)包的SQLite后端来设置/读取Redis实例。您可以通过环境使用包局部变量。这些变量将可用于包中的多个函数,但用户无法(轻松)访问,并且不会干扰用户工作区。一个简单的例子是:
pkg.env <- new.env()
pkg.env$cur.val <- 0
pkg.env$times.changed <- 0
inc <- function(by=1) {
pkg.env$times.changed <- pkg.env$times.changed + 1
pkg.env$cur.val <- pkg.env$cur.val + by
pkg.env$cur.val
}
dec <- function(by=1) {
pkg.env$times.changed <- pkg.env$times.changed + 1
pkg.env$cur.val <- pkg.env$cur.val - by
pkg.env$cur.val
}
cur <- function(){
cat('the current value is', pkg.env$cur.val, 'and it has been changed',
pkg.env$times.changed, 'times\n')
}
inc()
inc()
inc(5)
dec()
dec(2)
inc()
cur()
pkg.env您还可以创建令牌列表,并使用usethis::use_data(…,internal=TRUE)
将其添加到R/sysdata.rda。此文件中的数据是内部数据,但可由所有函数访问。如果您只想让某些函数访问令牌,则会出现唯一的问题,最好通过以下方式解决:
上述已提出的环境解决方案;或
创建一个隐藏的helper函数来保存令牌并返回它们。然后,只需在使用令牌的函数中调用这个隐藏函数(假设它是一个列表),就可以使用list2env(…,envir=environment())
将它们注入到它们的环境中
如果您不介意向包中添加依赖项,那么可以使用来自的R6
对象,正如@greg snow的答案中的注释所建议的那样
R6
对象是可以添加公共和私有方法的实际环境,非常轻量级,是共享包的全局变量的一个好的、更严格的选项,而不会污染全局环境
与@gregsnow的解决方案相比,它允许对变量进行更严格的控制(例如,您可以添加检查类型的方法)。缺点可能是依赖性,当然还有学习R6
语法
库(R6)
MyPkgOptions=R6::R6Class(
“mypkg_选项”,
公共=列表(
get_option=函数(x)private$.options[[x]]
),
活动=列表(
var1=函数(x){
if(缺少(x))private$.options[['var1']]
else stop(“这是一个无法更改的环境参数”)
}
,var2=函数(x){
if(缺少(x))private$.options[['var2']]
else stop(“这是一个无法更改的环境参数”)
}
),
私有=列表(
.options=列表(
var1=1,
var2=2
)
)
)
#创建一个实例
mypkg_options=mypkg选项$new()
#从活动字段获取值
mypkg_选项$var1
#> [1] 1
mypkg_选项$var2
#> [1] 2
#替代方法
mypkg_选项$get_选项(“var1”)
#> [1] 1
mypkg_选项$get_选项(“var3”)
#>空的
#变量将被锁定,除非您添加了更改它们的方法
mypkg_选项$var1=3
#>(函数(x)中出错:这是一个无法更改的环境参数
由(v0.3.0)于2020年5月27日创建您能详细说明您的具体情况吗?然后我们可以帮助您找到替代方案,最好是……谢谢您的回答。我有编程经验,并且知道全局变量通常是无效的。但是,我正在建立对服务的API访问,为了保持与此服务的连接,函数需要一些这些令牌应该是所有函数都可以访问的,我想到的是创建一个.RData文件来存储这些数据,但这似乎是一个糟糕的想法。正常的R模式是使用某种“handle”对象来保存令牌,并将该句柄传递给函数。这也允许您有多个并发会话使用不同的标记。例如,这就是数据库访问的模式。我认为你关于为什么全局变量是邪恶的论点需要对R进行一些调整-你在包中创建的所有函数都是全局变量。它们是邪恶的吗?;)所有全局变量都是邪恶的,但有些比其他变量更邪恶;)。引用类似乎是一种更为经典的面向对象方法。这将允许对象方法(函数)也是本地的。这将存储在哪里?@Rimbaud在名为的pairlist中。Options
位于base
包中。它存储在加载包的R会话的全局选项列表中。请参见?选项
。这是一个有用的实践,我想补充一点,作为将环境创建为可变容器时的安全措施,一个