【问题标题】:Create environment having access to libraries but not to .GlobalEnv创建可以访问库但不能访问 .GlobalEnv 的环境
【发布时间】:2020-09-15 15:15:34
【问题描述】:

我想在环境中评估一些代码 访问库(.GlobalEnv 以上的所有环境)但没有 访问在.GlobalEnv 中创建的对象。 我尝试了几种解决方案,但似乎都没有按预期工作

1。 .GlobalEnv 中的新环境

.GlobalEnv创建的这里环境可以访问其他 .GlobalEnv 中的对象。

.GlobalEnv$ee <- environment()
eval( 
  parse(text = "library(dplyr);mutate(iris, x = 1)"), 
  envir = .GlobalEnv$ee
)
var_in_global <- "x"
eval(
  expr = parse(text = "ls()"), 
  envir = .GlobalEnv$ee
) # expect empty
eval(
  expr = parse(text = "print(var_in_global)"), 
  envir = ee
) # expect error

2。作为 .GlobalEnv 子的新环境

这个编码方式不同,但结果与第一个相同

ee <- new.env(parent = globalenv())
eval(
  parse(text = "library(dplyr);mutate(iris, x = 1)"), 
  envir = ee
)
var_in_global <- "x"
eval(
  expr = parse(text = "ls()"), 
  envir = ee
)
eval(
  expr = parse(text = "print(var_in_global)"), 
  envir = ee
)

3。作为全局父级的新环境

在这种情况下,环境无权访问 .GlobalEnv 对象 但是之后加载的库会附在下面,所以环境不会 也可以访问这些库。

ee <- new.env(parent = parent.env(globalenv()))
eval(
  parse(text = "library(dplyr);mutate(iris, x = 1)"), 
  envir = ee
)
var_in_global <- "x"
eval(
  expr = parse(text = "ls()"), 
  envir = ee
)
eval(
  expr = parse(text = "print(var_in_global)"), 
  envir = ee
)

4。使用库(..., pos = 3)

使用@Allan Cameron 的回答,我试图让这段代码正常工作。并且library(dplyr);mutate(...) 在新环境中被正确评估。

ee <- new.env(parent = parent.env(globalenv()))
eval(
  parse(text = "library <- function(...) base::library(..., pos = 3)"),
  envir = ee
)
eval(
  parse(text = "library(dplyr);mutate(iris, x = 1)"), 
  envir = ee
)
var_in_global <- "x"
eval(
  expr = parse(text = "ls()"), 
  envir = ee
)
eval(
  expr = parse(text = "print(var_in_global)"), 
  envir = ee
)

但是,问题要深得多。一些包有更多的依赖项,这些依赖项是一起加载的。考虑这个例子,我用library(Hmisc);impute(...) 替换了library(dplyr);mutate()。此示例失败 - 找不到 impute 函数(这是错误的)

ee <- new.env(parent = parent.env(globalenv()))
eval(
  parse(text = "library <- function(...) base::library(..., pos = 3)"),
  envir = ee
)
eval(
  parse(text = "library(Hmisc);impute(iris[,1], 1)"), 
  envir = ee
) # expect to work

你有什么想法如何制作一个环境 与全局“并行”节点,并且之前还附加了库?

【问题讨论】:

    标签: r


    【解决方案1】:

    问题是当你调用library时,默认情况下它会在搜索路径中附加pos = 2的包:

    位置
    在搜索列表中附加加载的命名空间的位置。也可以是当前搜索列表中的位置名称,由 search() 给出。

    所以,当我开始 R 会话并执行 search() 时,我得到:

    #>  [1] ".GlobalEnv"        "tools:rstudio"     "package:stats"    
    #>  [4] "package:graphics"  "package:grDevices" "package:utils"    
    #>  [7] "package:datasets"  "package:methods"   "Autoloads"        
    #> [10] "package:base"
    

    当我打电话给library(dplyr) 然后重复search(),我得到:

    #>  [1] ".GlobalEnv"        "package:dplyr"     "tools:rstudio"    
    #>  [4] "package:stats"     "package:graphics"  "package:grDevices"
    #>  [7] "package:utils"     "package:datasets"  "package:methods"  
    #> [10] "Autoloads"         "package:base"  
    

    因此,如果您在附加任何包之前使ee 与全局环境具有相同的父级,那么您将遇到包放置在全局环境和ee 之间 的问题。 s 进入搜索路径。

    有几种方法可以解决这个问题,但也许最简单的方法是启动一个新的 R 会话并定义:

    library <- function(...) base::library(..., pos = 3)
    

    这确保加载到全局工作区中的包始终放置在ee 搜索路径的“入口点”之后。这会产生所需的行为:

    ee <- new.env(parent = parent.env(globalenv()))
    
    var_in_global <- "x"
    
    eval(expr = parse(text = "ls()"), envir = ee)
    #> character(0)
    
    eval(expr = parse(text = "print(var_in_global)"), envir = ee)
    #> Error in print(var_in_global): object 'var_in_global' not found
    
    library(dplyr) # Note this is called in the global environment
    
    eval(parse(text = "head(mutate(iris, n = 1), 5)"), envir = ee)
    #>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species n
    #> 1          5.1         3.5          1.4         0.2  setosa 1
    #> 2          4.9         3.0          1.4         0.2  setosa 1
    #> 3          4.7         3.2          1.3         0.2  setosa 1
    #> 4          4.6         3.1          1.5         0.2  setosa 1
    #> 5          5.0         3.6          1.4         0.2  setosa 1
    

    请注意,作为一个(可能是可取的)副作用,由于修改后的library 函数是在全局环境中定义的,那么如果从ee 内部调用library,它将被调度为base::library ,因此在ee 中加载的任何包都只能由ee 访问。

    编辑

    如果您希望在ee 中调用的代码影响全局搜索路径以及ee 的搜索路径,您可以尝试:

    ee <- new.env(parent = parent.env(globalenv()))
    
    ee$library <- function(...) {
      mc <- match.call()
      mc[[1]] <- quote(base::library)
      eval(mc, envir = globalenv())
      this_env <- parent.frame()
      if(!identical(this_env, globalenv())) 
      parent.env(this_env) <- parent.env(globalenv())
    }
    

    这给了我们:

    eval(
      parse(text = "library(Hmisc);impute(iris[,1], 1)"), 
      envir = ee
    )  
    
    #>  [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4 5.1 5.7
    #>  [20] 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5 4.9 5.0 5.5 4.9
    #>  [39] 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0 6.4 6.9 5.5 6.5 5.7 6.3
    #>  [58] 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6
    #>  [77] 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6
    #>  [96] 5.7 5.7 6.2 5.1 5.7 6.3 5.8 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7
    #> [115] 5.8 6.4 6.5 7.7 7.7 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4
    #> [134] 6.3 6.1 7.7 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9
    

    但是,我们需要小心。如果在全局环境中调用library,这将不会更新ee 的搜索路径。所以我们需要一个全局函数,比如:

    library <- function(...) {
      base::library(...)
      parent.env(ee) <- parent.env(globalenv())
    }
    

    当然,让您自己的包执行此操作会更好。这样一来,您就可以拥有一个单独的 library 函数来测试其调用框架并分派适当的方法,而无需让这些空闲的 library 函数在工作空间中浮动。

    reprex package (v0.3.0) 于 2020 年 9 月 15 日创建

    【讨论】:

    • 我只添加一件小事。 eval(quote(library
    • 另一件要记住的是,库不能在 baseenv 中被覆盖
    • 我需要在 ee 环境中评估 library(...)。此外,如果您更改代码以加载 library(Hmisc) 并评估 eval(parse(text = "impute(iris[,1], 1)"), envir = ee) - 这也会失败。
    • @GoGonzo 你的意思是如果你从ee 调用library 它应该使包在全球环境和ee 中都可用?
    • 这条线是杰作parent.env(this_env) &lt;- parent.env(globalenv())。在我看来,这个解决方案很有可能是正确的。但是,应该一如既往地进行适当的测试。
    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-04
    相关资源
    最近更新 更多