R中包中的全局变量

Posted

技术标签:

【中文标题】R中包中的全局变量【英文标题】:Global variables in packages in R 【发布时间】:2012-09-17 20:57:22 【问题描述】:

我正在用 R 开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量?

我读过一些关于环境的东西,但我不明白它是如何工作的,如果这甚至是处理事情的方式。

【问题讨论】:

您能否详细说明您的具体情况?然后我们可以帮助您找到替代方案,最好... ***.com/questions/5526322/… 【参考方案1】:

您可以通过环境使用包局部变量。这些变量将可用于包中的多个函数,但用户无法(轻松)访问,并且不会干扰用户的工作空间。一个快速简单的例子是:

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()

【讨论】:

这是一种有用的做法,我要补充一点,作为将环境创建为可变容器时的一项安全措施,通常应将父环境设置为emptyenv(),以防止意外拾取搜索路径中较高的值:因此new.env(parent = emptyenv()),而不仅仅是new.env() 另一个附录 - 在最新版本的 R 中,您可能需要使用 assign('key', value, pkg.env) 而不是 pkg.env$key &lt;- value,因为 pkg.env 通常是一个锁定的环境。 似乎每次获取包时都会创建一个新环境(pkg.env)。如果多次执行“required(pkg)”,这可能会增加内存占用。有没有办法避免呢? @dabsingh,我还没有看过require 的源代码以及它所做的一切。但是require 的帮助页面说它不会重新加载已经加载的命名空间(详细信息下第一段的中间)。所以,我不认为每次都会创建一个新环境。 感谢@GregSnow,到目前为止还没有意识到可以在包中包含一个打开的脚本 :) 这对我来说是一种启发!【参考方案2】:

您可以设置option,例如

options("mypkg-myval"=3)
1+getOption("mypkg-myval")
[1] 4

【讨论】:

这将存储在哪里? @Rimbaud 在名为 .Options 的配对列表中,位于 base 包中。 这存储在加载包的 R 会话的全局选项列表中。见?options 我会说这通常不是一个好习惯,因为任何人都可以在任何地方编辑 R 中的选项 - 很难确定您的下游依赖项之一没有编辑两者之间的值执行和下一个;甚至更难让您的几个下游依赖项进行互操作而不干扰其他人对选项的修改。这种方法肯定有用例,但 IMO 需要高度谨慎【参考方案3】:

一般来说,全局变量是邪恶的。它们邪恶的根本原则是您希望尽量减少包中的互连。这些互连通常会导致函数产生副作用,即它不仅取决于输入参数的结果是什么,还取决于某些全局变量的值。尤其是当函数数量增加时,这可能很难正确进行调试。

R 中的全局变量见SO post。

编辑以回应您的评论: 另一种方法可能是将所需信息传递给需要它的函数。您可以创建一个包含此信息的新对象:

token_information = list(token1 = "087091287129387",
                         token2 = "UA2329723")

并要求所有需要此信息的函数将其作为参数:

do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)

这样从代码中可以看出函数中需要token信息,可以自行调试函数。此外,该函数没有副作用,因为它的行为完全由它的输入参数决定。典型的用户脚本如下所示:

token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)

我希望这能让事情更清楚。

【讨论】:

感谢您的回答。我有编程经验,并且知道全局变量通常是一个nogo。但是,我正在建立对服务的 API 访问,为了保持与该服务的连接,这些功能需要几个令牌。这些令牌应该可以被所有函数访问,我想出的是创建一个 .RData 文件来存储这些数据,但这似乎不是一个好主意。 正常的 R 模式是拥有某种“句柄”对象来保存您的令牌,并将该句柄传递给您的函数。这也使您可以拥有多个具有不同令牌的并发会话。例如,这就是数据库访问的模式。 我认为你关于为什么全局变量是邪恶的论点需要对 R 进行一些调整——你在包中创建的所有函数都是全局变量。他们是邪恶的吗? ;) 所有全局变量都是邪恶的,但有些比其他的更邪恶;)。引用类似乎是一种更经典的面向对象的方法。这将允许对象方法(函数)也是本地的。【参考方案4】:

问题不清楚:

只有一个 R 进程还是多个?

仅在一台主机上,还是跨多台机器?

它们之间是否有共同的文件访问?

按照复杂度递增的顺序,我会使用文件、通过RSQlite 包或(我最喜欢的:)rredis 包的SQLite 后端来设置/读取Redis 实例。

【讨论】:

【参考方案5】:

您还可以创建一个令牌列表并将其添加到 R/sysdata.rda 并使用usethis::use_data(..., internal = TRUE)。此文件中的数据是内部数据,但所有函数均可访问。如果您只希望 some 函数访问令牌,则会出现唯一的问题,这将更好地服务于:

    上面已经提出的环境解决方案;或 创建一个隐藏的辅助函数来保存令牌并返回它们。然后只需在使用令牌的函数中调用这个隐藏函数,并且(假设它是一个列表)您可以使用 list2env(..., envir = environment()) 将它们注入到它们的环境中。

【讨论】:

【参考方案6】:

如果您不介意向您的包添加依赖项,您可以使用来自homonym package 的R6 对象,正如@greg-snow 的答案中的cmets 所建议的那样。

R6 对象是实际环境,可以添加公共和私有方法,非常轻量级,可以成为共享包的全局变量的良好且更严格的选择,而不会污染全局环境。

与@greg-snow 的解决方案相比,它允许对变量进行更严格的控制(例如,您可以添加检查类型的方法)。缺点可能是依赖关系,当然还有学习R6 语法。

library(R6)
MyPkgOptions = R6::R6Class(
  "mypkg_options",
  public = list(
    get_option = function(x) private$.options[[x]]
  ),
  active = list(
    var1 = function(x)
      if(missing(x)) private$.options[['var1']]
      else stop("This is an environment parameter that cannot be changed")
    
    ,var2 = function(x)
      if(missing(x)) private$.options[['var2']]
      else stop("This is an environment parameter that cannot be changed")
    
  ),
  private = list(
    .options = list(
      var1 = 1,
      var2 = 2
    )
  )
)
# Create an instance
mypkg_options = MyPkgOptions$new()
# Fetch values from active fields
mypkg_options$var1
#> [1] 1
mypkg_options$var2
#> [1] 2
# Alternative way
mypkg_options$get_option("var1")
#> [1] 1
mypkg_options$get_option("var3")
#> NULL
# Variables are locked unless you add a method to change them
mypkg_options$var1 = 3
#> Error in (function (x) : This is an environment parameter that cannot be changed

由reprex package (v0.3.0) 于 2020-05-27 创建

【讨论】:

以上是关于R中包中的全局变量的主要内容,如果未能解决你的问题,请参考以下文章

管道符作业控制变量定义全局变量环境变量

在R中的引用类中设置全局变量

为啥 R 中的 ls() 不显示全局变量?

C语言全局变量(c文件和h文件中的全局变量静态全局变量)使用注意事项

C语言全局变量(c文件和h文件中的全局变量静态全局变量)使用注意事项

R CMD检查中的全局变量注释没有可见的绑定