用于分离R环境的源脚本,而不是全局环境

时间:2021-11-04 12:12:35

Is there a way to source() a script in R such that it is attached as a parent to the global environment (.GlobalEnv)?

有没有办法在R中源()一个脚本,以便它作为父对象附加到全局环境(.GlobalEnv)?

Currently, when I source a script, all variables and functions of that script appear in my global (interactive) environment. I'd like to include these variables and functions in the search path, but not in .GlobalEnv. That is, I'd like the sourced script to behave like an attached package, which gets attached between the global and base environments (see figure from Advanced R Environments)

目前,当我获取脚本时,该脚本的所有变量和函数都出现在我的全局(交互)环境中。我想在搜索路径中包含这些变量和函数,但不包含在.GlobalEnv中。也就是说,我希望源脚本的行为类似于附加的包,它附加在全局环境和基础环境之间(参见高级R环境中的图)

用于分离R环境的源脚本,而不是全局环境

4 个解决方案

#1


3  

From the source documentation, the local argument can be an environment which determines where the sourced expressions are evaluated.

从源文档中,local参数可以是一个确定源表达式求值位置的环境。

This suggests that you could create a new environment, run source passing this environment to local, then attach the environment to the search path.

这表明您可以创建一个新环境,运行将此环境传递给本地的源,然后将环境附加到搜索路径。

Or you can use attach with what=NULL to create an empty environment, save the return value, and pass that to local in source:

或者您可以使用attach with what = NULL来创建一个空的环境,保存返回值,并将其传递给源中的本地:

tmp <- attach(what=NULL)
source('test.R', local=tmp)

or as a single line:

或作为一行:

source('test.R', local=attach(NULL))

#2


3  

The simplest way to source a script as if it was a package (i.e. such that lexical scoping won't result in the use of variables defined in the global environment when calling functions defined in your R script) is to create an environment that that is whose parent is the .BaseNamespaceEnv, and then call source() using that environment.

获取脚本的最简单方法就好像它是一个包(即,当调用R脚本中定义的函数时,词法作用域不会导致使用在全局环境中定义的变量)是创建一个环境,即其父级是.BaseNamespaceEnv,然后使用该环境调用source()。

For example if you have a script like this:

例如,如果您有这样的脚本:

# << my-script.R >>
my_fun <- function(x){x + y}

Then evaluating the following at the console, won't generate an error, as it would if my_fun were defined within it's own package:

然后在控制台上评估以下内容将不会产生错误,因为如果在其自己的包中定义了my_fun:

source("my-script.R")
y = 2
my_fun(1)
#> 3

However, if you create an environment whose search() path does not include the Global Environment (.GlobalEnv) then you'll get a proper error when you call the function from your script:

但是,如果您创建一个search()路径不包含全局环境(.GlobalEnv)的环境,那么当您从脚本调用该函数时,您将收到正确的错误:

# Create the environment:
ENV = new.env(parent = .BaseNamespaceEnv)
# Attache it to the search path so that objects in your environment can be
# found from the global environment (i.e. from the console):
attach(ENV)
# do things:
source("my-script.R",ENV)
y = 2
my_fun(1)
#> Error in .ENV$my_fun(3) : object 'y' not found

#3


2  

The following environment insertion appears to achieve the desired functionality, however, I'm not sure it's the best way to do this:

以下环境插入似乎可以实现所需的功能,但是,我不确定这是最好的方法:

Check the current search path:

检查当前搜索路径:

search()
# [1] ".GlobalEnv"        "package:stats"     "package:graphics"
# [4] "package:grDevices" "package:utils"     "package:datasets"
# [7] "package:methods"   "Autoloads"         "package:base"

Add new environment for sourced packages and use local parameter when source()ing:

为源包添加新环境并在source()时使用local参数:

attach(new.env(), name="sourced_scripts")
myEnv <- as.environment("sourced_scripts")

source("some_other_script.R", local=myEnv)

search()
#  [1] ".GlobalEnv"        "package:dplyr"     "sourced_scripts"
#  [4] "package:stats"     "package:graphics"  "package:grDevices"
#  [7] "package:utils"     "package:datasets"  "package:methods"
# [10] "Autoloads"         "package:base"

Our script has added the dplyr package to the search path, but note that "package:dplyr" environment precedes the sourced script environment.

我们的脚本已将dplyr包添加到搜索路径,但请注意“package:dplyr”环境位于源脚本环境之前。

In order for sourced functions to be able to use dplyr (and any other packages), we remove the "sourced_script" environment and reattach it at the front of the search path, ahead of the packages attached by the sourced script. Note: using attach() to do this will not work because attach() inserts a copy of the input environment (in this case, myEnv).

为了使源代码函数能够使用dplyr(和任何其他包),我们删除“sourced_script”环境并将其重新附加到搜索路径的前面,在源代码脚本附加的包之前。注意:使用attach()执行此操作将不起作用,因为attach()插入输入环境的副本(在本例中为myEnv)。

detach("sourced_scripts")
parent.env(myEnv) <- parent.env(.GlobalEnv)
parent.env(.GlobalEnv) <- myEnv
rm(myEnv)  # at this point we can remove myEnv to clear up namespace

search()
#  [1] ".GlobalEnv"        "sourced_scripts"   "package:dplyr"
#  [4] "package:stats"     "package:graphics"  "package:grDevices"
#  [7] "package:utils"     "package:datasets"  "package:methods"
# [10] "Autoloads"         "package:base"

#4


2  

I would also like to a solution using sys.source function. Using envir and toplevel.env arguments allows for convenient (IMHO) bypassing of the global environment. As per the linked documentation:

我还想使用sys.source函数的解决方案。使用envir和toplevel.env参数可以方便地(恕我直言)绕过全局环境。根据链接文档:

sys.source [p]arses expressions in the given file, and then successively evaluates them in the specified environment.

sys.source [p]评估给定文件中的表达式,然后在指定的环境中连续评估它们。

tstEnv <- new.env()
sys.source(file = "tst.R", envir = tstEnv, toplevel.env = tstEnv)

where tst.R contains:

其中tst.R包含:

a <- 1
b <- 1

Results:

ls(envir = .GlobalEnv)
# [1] "tstEnv"
ls(envir = tstEnv)
# [1] "a" "b"
tstEnv$a
# [1] 1

#1


3  

From the source documentation, the local argument can be an environment which determines where the sourced expressions are evaluated.

从源文档中,local参数可以是一个确定源表达式求值位置的环境。

This suggests that you could create a new environment, run source passing this environment to local, then attach the environment to the search path.

这表明您可以创建一个新环境,运行将此环境传递给本地的源,然后将环境附加到搜索路径。

Or you can use attach with what=NULL to create an empty environment, save the return value, and pass that to local in source:

或者您可以使用attach with what = NULL来创建一个空的环境,保存返回值,并将其传递给源中的本地:

tmp <- attach(what=NULL)
source('test.R', local=tmp)

or as a single line:

或作为一行:

source('test.R', local=attach(NULL))

#2


3  

The simplest way to source a script as if it was a package (i.e. such that lexical scoping won't result in the use of variables defined in the global environment when calling functions defined in your R script) is to create an environment that that is whose parent is the .BaseNamespaceEnv, and then call source() using that environment.

获取脚本的最简单方法就好像它是一个包(即,当调用R脚本中定义的函数时,词法作用域不会导致使用在全局环境中定义的变量)是创建一个环境,即其父级是.BaseNamespaceEnv,然后使用该环境调用source()。

For example if you have a script like this:

例如,如果您有这样的脚本:

# << my-script.R >>
my_fun <- function(x){x + y}

Then evaluating the following at the console, won't generate an error, as it would if my_fun were defined within it's own package:

然后在控制台上评估以下内容将不会产生错误,因为如果在其自己的包中定义了my_fun:

source("my-script.R")
y = 2
my_fun(1)
#> 3

However, if you create an environment whose search() path does not include the Global Environment (.GlobalEnv) then you'll get a proper error when you call the function from your script:

但是,如果您创建一个search()路径不包含全局环境(.GlobalEnv)的环境,那么当您从脚本调用该函数时,您将收到正确的错误:

# Create the environment:
ENV = new.env(parent = .BaseNamespaceEnv)
# Attache it to the search path so that objects in your environment can be
# found from the global environment (i.e. from the console):
attach(ENV)
# do things:
source("my-script.R",ENV)
y = 2
my_fun(1)
#> Error in .ENV$my_fun(3) : object 'y' not found

#3


2  

The following environment insertion appears to achieve the desired functionality, however, I'm not sure it's the best way to do this:

以下环境插入似乎可以实现所需的功能,但是,我不确定这是最好的方法:

Check the current search path:

检查当前搜索路径:

search()
# [1] ".GlobalEnv"        "package:stats"     "package:graphics"
# [4] "package:grDevices" "package:utils"     "package:datasets"
# [7] "package:methods"   "Autoloads"         "package:base"

Add new environment for sourced packages and use local parameter when source()ing:

为源包添加新环境并在source()时使用local参数:

attach(new.env(), name="sourced_scripts")
myEnv <- as.environment("sourced_scripts")

source("some_other_script.R", local=myEnv)

search()
#  [1] ".GlobalEnv"        "package:dplyr"     "sourced_scripts"
#  [4] "package:stats"     "package:graphics"  "package:grDevices"
#  [7] "package:utils"     "package:datasets"  "package:methods"
# [10] "Autoloads"         "package:base"

Our script has added the dplyr package to the search path, but note that "package:dplyr" environment precedes the sourced script environment.

我们的脚本已将dplyr包添加到搜索路径,但请注意“package:dplyr”环境位于源脚本环境之前。

In order for sourced functions to be able to use dplyr (and any other packages), we remove the "sourced_script" environment and reattach it at the front of the search path, ahead of the packages attached by the sourced script. Note: using attach() to do this will not work because attach() inserts a copy of the input environment (in this case, myEnv).

为了使源代码函数能够使用dplyr(和任何其他包),我们删除“sourced_script”环境并将其重新附加到搜索路径的前面,在源代码脚本附加的包之前。注意:使用attach()执行此操作将不起作用,因为attach()插入输入环境的副本(在本例中为myEnv)。

detach("sourced_scripts")
parent.env(myEnv) <- parent.env(.GlobalEnv)
parent.env(.GlobalEnv) <- myEnv
rm(myEnv)  # at this point we can remove myEnv to clear up namespace

search()
#  [1] ".GlobalEnv"        "sourced_scripts"   "package:dplyr"
#  [4] "package:stats"     "package:graphics"  "package:grDevices"
#  [7] "package:utils"     "package:datasets"  "package:methods"
# [10] "Autoloads"         "package:base"

#4


2  

I would also like to a solution using sys.source function. Using envir and toplevel.env arguments allows for convenient (IMHO) bypassing of the global environment. As per the linked documentation:

我还想使用sys.source函数的解决方案。使用envir和toplevel.env参数可以方便地(恕我直言)绕过全局环境。根据链接文档:

sys.source [p]arses expressions in the given file, and then successively evaluates them in the specified environment.

sys.source [p]评估给定文件中的表达式,然后在指定的环境中连续评估它们。

tstEnv <- new.env()
sys.source(file = "tst.R", envir = tstEnv, toplevel.env = tstEnv)

where tst.R contains:

其中tst.R包含:

a <- 1
b <- 1

Results:

ls(envir = .GlobalEnv)
# [1] "tstEnv"
ls(envir = tstEnv)
# [1] "a" "b"
tstEnv$a
# [1] 1