在 instance_eval 中访问 Ruby 实例变量

3

我正在尝试一些Ruby元编程,并且在使用instance_eval()时有些困惑。

请看以下示例:

@instance_var = 'instance_var'
local_var = 'local_var'
obj = Object.new
obj.instance_eval { p @instance_var; p local_var }
obj.instance_eval { @instance_var  = 'instance_var_in_obj'; local_var = 'local_var_in_obj' }
p @instance_var; p local_var

我希望@instance_var和local_var两者都可以在代码块中传递或修改,但是我得到了以下结果:
nil
"local_var"
"instance_var"
"local_var_in_obj"

作为结果,我们可以在 instance_val 中分享(传递/修改)本地变量,但是实例变量属于 self 不能共享。
关于 instance_exec:
obj.instance_exec(@instance_var) {|instance_var| p instance_var; instance_var = @instance_var }
=> "instance_var"
@instance_var
=> "instance_var"

现在我可以传递我的外部实例变量,但仍然无法修改它。

@instance_arr = []
obj.instance_exec(@instance_arr) {|instance_arr| instance_arr << 'in_block' }
@instance_arr
=> ["in_block"]
obj.instance_exec(@instance_arr) {|instance_arr| instance_arr = [] }
@instance_arr
=> ["in_block"]

使用数组实例变量可以修改实例变量,但仅限于当前数组对象。

总之,应该使用带有局部变量而非实例变量的instance_evalinstance_exec方法?

是否有我遗漏的概念?


我可以提供一半的答案。你有两个实例变量命名为@instance_var,一个用于我们称之为“main”的Object实例,另一个是obj。在obj.instance_eval { p @instance_var; p local_var }块中,selfobj,该对象的@instance_var尚未初始化,因此在引用时返回nil。我不完全理解为什么局部变量会被区别对待,但我怀疑这与引用已初始化的局部变量会引发异常有关。 - Cary Swoveland
谢谢你的回答。关于@instance_var@表示你属于某个人,我想我可以理解这一部分。是的,我的问题的重点是“为什么局部变量被处理得不同”,或者说“局部变量”实际上属于什么,如何在作用域中思考它? - Arthur H
2个回答

2

经过一些搜索和朋友的建议,我认为我找到了问题所在。 在Ruby中,当你的代码运行时,有两个上下文selfbinding。当你使用local varsmethod而没有设置self.xxx时,首先要检查的是它是否在binding对象中作为local var存在,如果不存在,Ruby将认为它是一个方法,然后在self对象中搜索其定义并调用它。 可以这样思考:

class A
  def test
    4
  end
  def use_variable
    test = 5
    test
  end
  def use_method
    test = 5
    self.test
  end
end
a = A.new
a.use_variable # returns 5
a.use_method   # returns 4

正如其文档所述,instance_eval只是在给定的块中更改了self而没有触及binding,因此方法将在新的self上搜索,本地值仍然在同一个binding对象中。

关于 instance_exec,我不是很确定,但似乎实例变量(带有 at 前缀的变量)将直接在 self 上搜索,跳过 binding,因此在 instance_exec 之外,你的 @instance_arr 属于旧的 self,而在 instance_exec 块中,你将其作为新的 local var 在块的新 binding 中获取(块具有自己的作用域),但它的值实际上是 @instance_arr 的引用,因此在新的 local var 上调用方法,例如 push,它们两个都会改变,因为它们共享同一个 Array instance,但当你将新的 Array instance 分配给新的 local var 时,它们不再引用相同的 Array instance,这就是第二个 WHY

0
为了评估局部变量,您需要传递字符串“local_var”,它将返回局部变量的值。如果传入一个块,则不能传入参数,根据我的解释文档
实例eval在块形式中的行为是作为闭包访问该调用所在对象的实例变量和私有方法。
使用参数的实例eval的行为允许您在该调用的范围内评估字符串。

谢谢你的回答,你的解释是正确的,我只是没有考虑到“绑定”这个概念,它可以解释问题。 :) - Arthur H

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