如何在加载时将变量提供给命名空间

15

在我的某个软件包中,我使用.onAttach钩子运行一些R代码,然后使用assign将值作为软件包变量之一提供。我这样做是因为variable取决于某个文件的内容,这个文件可能会在一个会话和另一个会话之间发生变化。 我使用的代码如下:

.onAttach <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = as.environment("package:MyRPackage"))
}

当我使用 library(MyRpackage) 命令调用包后,我就可以使用 variable 了。

但是如果没有预先使用 library(MyRpackage) 命令加载包,那么就无法使用 MyRPackage::variable

我知道这是因为我应该将这段代码放在 .onLoad 钩子函数中,但是我无法进行正确的赋值以使其生效。

我尝试过:

.onLoad <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = as.environment("namesoace:MyRPackage"))
}

.onLoad <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = asNamespace("MyRPackage"))
}

但是当我在不使用library附加软件包的情况下运行MyRPackage:::variable时,它们两个都会出现一些错误。

.onLoad钩子中正确的赋值方法是什么?


1
你能解释一下为什么你想在每次加载时重新计算变量吗? - Dason
1
你能够导出some_function,以便可以调用MyRPackage::some_function()吗? - chinsoon12
@lucacerone 那个文件会在每个会话中更改吗? - Dason
我不确定在加载包之前是否可以使用 MyRPackage:::variable,因为只有在实际加载包时才会分配 variable。这是我不理解的事情吗? - F. Privé
F. Privé 我想要加载这个包,但不一定要附加它。对于所有其他导出的函数,您可以使用 pkg::function 的方式调用,但是对于在 .onLoad 中创建的函数,我没有找到方法。 - lucacerone
显示剩余2条评论
2个回答

20

按照相关问题中 此答案 的方法,您可以像这样更改.onLoad()函数:

.onLoad <- function(libname, pkgname) {
    variable <- some_function()
    assign("variable", variable, envir = parent.env(environment()))
}

然后,您可以使用MyRPackage:::variable访问变量而不附加软件包。我不知道您如何使用some_function(),所以我尝试使用虚拟软件包进行以下操作:

.onLoad <- function(libname, pkgname) {
    variable <- 42
    assign("variable", variable, envir = parent.env(environment()))
}

在一个新的R会话中,结果是

> MyRPackage:::variable
[1] 42

进一步解释

摘自 Hadley Wickham 的 Advanced R:

有四种特殊环境:

...

  • environment() 是当前环境。

...

您可以使用 ls() 列出环境框架中的绑定,并使用 parent.env() 查看其父级。

因此,如果我们进一步修改 .onLoad() 函数,就可以看到它的效果:

.onLoad <- function(libname, pkgname) {
    print(environment()) # For demonstration purposes only;
    print(parent.env(environment())) # Don't really do this.
    variable <- 42
    assign("variable", variable, envir = parent.env(environment()))
}

然后启动R会话会导致

<environment: 0x483da88>
<environment: namespace:MyRPackage>

在会话开始时,将文本打印到控制台。这使您可以在环境名称空间“MyRPackage”中分配变量,即使尝试使用“assign(“variable”,variable,envir = namespace:MyRPackage)”也会导致错误。

错误:加载“MyRPackage”包或名称空间失败:

  .onLoad失败于loadNamespace()中的'MyRPackage',详细信息:

  调用:get(name,envir = ns,inherits = FALSE)

  错误:找不到对象'namespace'

当安装软件包时。


谢谢duckymar,让我进行几次测试,然后我会告诉你它是否有效。 - lucacerone
它完美地运行了,谢谢 :) 你能否稍微改进一下答案,简要解释一下parent.env(environment())实际上是在做什么? - lucacerone
1
@lucacerone 我今天稍后会编辑以添加更多细节。 - duckmayr
1
@lucacerone 添加了一些进一步的解释。 - duckmayr

4

基本上有三种方法:

  1. 通过 assignInMyNamespace(…)
  2. 通过 assign(…, envir = topenv())
  3. 通过子集赋值:ns$name = value

虽然选项1似乎相当普遍,但实际上需要更多的代码,因为在使用assignInMyNamespace覆盖变量之前,您需要先创建一个变量:

myvar = NULL

.onLoad = function (libname, pkgname) {
    assignInMyNamespace('myvar', value)
}

如果未提前声明变量,将导致错误。

相反,assign 完全能够创建以前未声明的新变量。我们只需告诉 R 将变量分配到哪个环境中,函数 topenv() 提供了这个功能。

.onLoad = function (libname, pkgname) {
    assign('myvar', value, envir = topenv())
}

当然,如果我们定义了一个命名空间对象并对它进行子集分配,则不需要使用assign()(或assignInMyNamespace()):

.onLoad = function (libname, pkgname) {
    ns = topenv()
    ns$myvar = value
}

对于我的代码,我倾向于最后一种选择。


R文档表示:“assignInNamespace不应在最终代码中使用,并且将来如果从软件包中调用,将会抛出错误。已经禁止某些用法。目前,几乎所有软件包都不使用选项1。” - undefined
@LiangZhang assignInMyNamespaceassignInNamespace!不管怎样,我个人不推荐使用那个选项,原因在我的回答中已经提到,尽管它的使用相对较广泛。我总是使用直接赋值。 - undefined
啊,非常抱歉忽略了那个“我的”字!非常感谢。我喜欢你的ns$name = value方法。 - undefined
顺便说一下,在memoise::memoise()的文档中可能存在误导:它添加了一个带有fun <<- memoise::memoise(fun)语句的示例。也许可以按照您在这里的方法进行修正。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接