【问题标题】:How can I limit the scope of functions using source()?如何使用 source() 限制函数的范围?
【发布时间】:2019-08-08 12:44:46
【问题描述】:

我来自 python,其中 import 以更命名空间的风格表现,我对 R 的背景很少。

我正在尝试开发一个拆分为单独实体的 R 应用程序,但据我了解,R 没有像 python 中的导入。从我收集到的:

  • library 用于导入已安装的库,这些库具有自己的命名空间,因此可以使用include.only 降低导入 .R 时发生冲突的风险。
  • 如果您的代码是应用程序的一部分,而不是在外部库中,则必须使用源代码。从我收集到的信息来看,source 基本上相当于将 sourced.R 文件的全部内容放入 sourcing.R 文件中。

我在这里讨论第二种情况。不是第一个。我怀疑在这种情况下发生的情况是,如果您有多个具有相同符号的 sourced.R,它们将默默地发生冲突。从python的角度来看,它很像import *

以下是问题:

  1. 我是否正确地说在 sourced.R 中定义的函数都将进入全局环境,而不是它们自己的环境?
  2. 如果您对某项进行两次采购会怎样?是否包含两次?
  3. 是否有技术或最佳实践解决方案来防止来自恰好具有相同符号名称的源模块的意外冲突?

编辑

这是 G. Grothendieck 建议后的示例:

ex1.R

cat("hello")
source("whatever.R")
source("whatever.R", local=whatever <- new.env())
x()
whatever$x()
cat("whatever")
print(environment())
x <- function() {
    print("x")
}

因此,原则上,可以使用这种策略来确保函数不会全部被推到全局命名空间中并发生冲突。但是,它变成了导入代码的责任,另外,如果维护了任何状态并且两段代码来自同一个模块,它们最终会得到不同的环境,从而导致不同的状态。

底线是环境必须是无状态的。

【问题讨论】:

  • 您可以在环境中获取资源。请参阅 sourcelocal 参数。另请参阅 CRAN 上的模块包。
  • @G.Grothendieck 在这种情况下,我不太清楚本地的含义。在这种情况下,sourced.R 文件是否有自己独立的环境?
  • 是的。试试cat("a &lt;- 3", file = "testa.R"); source("testa.R", local = e &lt;- new.env()); ls(e)
  • @G.Grothendieck 经过实验,它不会将其绑定到不同的环境。本地仅在您从例如采购时才有效。在一个函数里面。如果您不包括本地,则无论如何 source 都会将符号绑定到全局环境。仅当您包含 local=TRUE 时,符号才会绑定到函数的环境
  • 你试过我的例子了吗????

标签: r


【解决方案1】:

对于您的问题:

  1. 在源脚本中创建的对象将在脚本为sourced 的环境中可用。因此,如果脚本是从全局环境中获取的,那么在源脚本中创建的对象将在全局环境中可用
  2. 如果您对某个内容进行了两次来源,则它会被包含一次。就像您运行 x &lt;- 4; x &lt;- 4 一样。在这种情况下,x 只会被包含一次
  3. @G。 Grothendieck 建议查看local 参数。您可能还会发现this 答案很有用(如果需要,可以显示全局分配的工作原理)。 Hadley's write up on environments 很全面。

如果有用,我们可以创建以下 2 个文件。只运行first.R 中的代码向我们展示了

  • a 变为 2,这表明源脚本中的赋值确实会覆盖其来源环境中的任何名称(这适用于对象/函数等)
  • c 等于 8 确认源脚本从其来源的环境中“看到”对象 b
# first.R
# rm(list=ls())
a <- 1
b <- 4
source("second.R", local = new.env())
# second.R
a <- 2 
new_func <- function(x) { x * 2 } 
c <- new_func(b) 
print(a)
print(c) 

【讨论】:

  • > “如果您两次获取某项内容,它会被包含一次”。不是从我的简要评估。如果你有 cat() 或 print(),你会收到两条消息,这意味着每次你需要使用另一个 .R 文件中的东西时,你要么必须依赖给定的代码已经被获取,因此生活在全局命名空间中(糟糕!)或在任何地方获取它,并像在 C 中一样具有双重包含保护。
  • @StefanoBorini 我同意print()cat()。但我相信后面的任何分配(函数或其他对象)都会简单地覆盖现有的同名对象?
  • 是的,但我的意思是,如果它的行为如此,您最终可能会遇到以下问题: 1. 具有循环依赖的代码将永远导入自身,直到内存耗尽 2. side多次产生效果 3. 如果两个模块碰巧有相同的命名例程,现在导入顺序很重要,这在大型应用程序中是一场噩梦
  • 我想我找到了一个简单的解决方案。请检查上面的更新示例。 source("second.R", local = new.env()) 创建一个新环境来运行源代码。现在我们看到在second.R运行的环境中a等于2(因为cat()为我们打印这个, but we also see that a`在父环境中没有改变(即它仍然等于1)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-26
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2014-05-15
  • 2023-03-04
  • 2023-04-04
相关资源
最近更新 更多