在related question中,我试图理解为什么赋值方法返回了一个意外的值,并且知道这是Ruby中的surprising but documented edge case。然而,当我试图调试这个问题时,我进一步深入兔子洞并遇到了一些我无法解释的额外惊喜。
当我在类中有一个setter方法时,例如:
class Setter
def foo=(bar)
@foo = Integer(bar).succ
end
end
然后我从setter方法获得返回值的文档奇数,但实例变量仍然正确设置。例如:
s = Setter.new
s.foo = 1
#=> 1
s.instance_variable_get :@foo
#=> 2
但是,在REPL(例如Pry或IRB)中,实例变量实际上从未被设置,即使我的理解是实例变量应该存储在顶层“主”对象中:
self.name
#=> NoMethodError: undefined method `name' for main:Object
# This is expected to set the @foo instance variable for main.
def foo= int
@foo = int
end
foo = 1
@foo
#=> nil
instance_variable_get :@foo
#=> nil
TOPLEVEL_BINDING.eval('self').instance_variables
#=> []
然而,toplevel对象 存储实例变量!例如:
@bar = 1 + 1; @bar
#=> 2
instance_variable_get :@bar
#=> 2
鉴于REPL存储实例变量,为什么类赋值方法在顶层赋值方法失败时有效?我希望两者的功能相同。
答案 0 :(得分:1)
=
将创建一个局部变量。在你的情况下:
foo = 1
正在创建一个局部变量 foo 而不是调用方法foo=
。你必须使用
self.foo = 1
实际调用上面定义的方法。现在,这将设置 @foo :
def foo= i # define foo= on self
@foo = i
end
#=> :foo=
foo = 3
#=> 3
@foo
#=> nil
foo # here's the new local variable
#=> 3
instance_variables
#=> [:@prompt]
instance_variable_get :@foo
#=> nil
self.foo = 4 # now calling the foo= method
#=> 4
foo # local foo is still 3
#=> 3
@foo # now the ivar is set
#=> 4
在您的班级示例中,您有一个s.foo = 1
的显式接收器。然后Ruby知道你在foo=
上调用s
setter。 assignment methods documentation说:
使用方法分配时,您必须始终拥有接收器。如果您没有接收器,Ruby假定您正在分配一个局部变量[。]