R:我可以在不改变主要环境的情况下运行source的代码吗?

问题描述

在R中,我想source()出于其他目的而编写的代码,以获取其创建的某些对象。但是我不希望该源代码覆盖我的主程序中的对象,也不希望该源代码加载可能掩盖我在主程序中使用的函数的库(例如,在dplyr之后加载plyr将如何掩盖某些dplyr函数) )。

在普通代码中(没有source()代码),我可以在子环境中运行一些代码而不会影响主要环境对象,而只是将选定的对象从子环境中传递回主要环境:

myGlobal <- "initial value"  # create an object in the main environment
objectsOfMyDesire <- with(new.env(),{
  myGlobal <- paste(myGlobal,"CHANGED by the with()"); message(paste0("in with(): myGlobal = ",myGlobal,"\""))  # alter object inherited from main environment
  mySub <- paste("mySub:",toupper(myGlobal)); message(paste0("in with(),mySub = ",mySub))    # create object
  return(list("mySub" = mySub))  # Return the created objects that I want to keep
})  # run code within a child environment,and return certain values to the main environment.

# Result: in the main environment,myGlobal is unchanged,mySub does not exist,but objectsOfMyDesire$mySub has been created
myGlobal; objectsOfMyDesire$mySub
mySub

但是当我获得完全相同的代码时,子环境中发生的一切都会影响主环境:

# Put code from within the "with()" above into a file,and then source() that file within a "with()":
rm(myGlobal,objectsOfMyDesire,mySub)
myTmpFile <- tempfile(pattern = "file",tmpdir = tempdir(),fileext = ".txt")
writeLines(con = myTmpFile,text = 'myGlobal <- paste(myGlobal,myGlobal));\n mySub <- paste("mySub:",mySub))') 

# Having created a file to source(),run the example above,replacing some of the "with()" code with "source(myTmpFile)":
myGlobal <- "initial value"  # create an object in the main environment
objectsOfMyDesire <- with(new.env(),{
  source(myTmpFile)
  return(list("mySub" = mySub))  # Return the created objects that I want to keep
})  # run code within a child environment,myGlobal is CHANGED,mySub EXISTS,and objectsOfMyDesire$mySub has been created
myGlobal; objectsOfMyDesire$mySub
mySub

file.remove(myTmpFile)  # delete the temporary file

即使没有source(),在with()内创建的库仍然存在:

# For this test,use some function from some package that you have installed,but not loaded in this session
exists("summarize")
with(new.env(),{
  message(paste("\n\nin with(),exists(\"summarize\")=",exists("summarize"),"\n\n"))
  library(dplyr)  # load the package that contains the function
  message(paste("\n\nin with(),"\n\n")) })
exists("summarize")  # The package that was loaded in the child environment persists into the main environment. This is not good. I do not want anything from the child environment to persist unless I explicitly specify it. 
#unloadNamespace("dplyr")

因此,有什么方法可以使我从主程序中source()进行编程,而不仅仅是源程序回传我明确指定的内容,而没有源程序影响主程序环境?

解决方法

我找到了部分解决方案:

# Call the program that creates some objects I want to use in the current program.
#   Run it in a new,child environment that I'll name calledProgram,so that it will not affect objects in the main environment.
source(file=programFileAndPath,local = calledProgram <- new.env(),echo=T)
# The called programs objects are now in the calledProgram object.
#   So I now add code to bring the objects I wanted from the called program into the main environment.
usefulDataFrame <- calledProgram$usefulDataFrame
niceGGPlot      <- calledProgram$niceGGPlot
rm(calledProgram)  # I have moved the objects that I need into the main environment,so I now delete the calledProgram object

这不会保护会话的搜索路径或命名空间,因此如果源代码添加了库、安装包或更改包版本,即使在运行此代码后,这些更改也会持续存在。但这至少可以防止被调用的程序在主程序的环境中创建或更改对象。