Ruby 关于 self 的隐含调用的问题

andrew_qx · 2011年11月25日 · 最后由 aNdReW_Qx 回复于 2012年02月04日 · 3638 次阅读

比如有一个 class

class Log
  include Mongoid::Document
  include Mongoid::Timestamps::Created

  field :task_ids, type: Array ,:default => []

  protected
  before_save :save_task_ids
  def save_task_ids
      task_ids = somevalue
  end
end

我测试下来 task_ids= 这句话被理解为给一个局部变量赋值了,如果是要给 field 赋值,必须是用 self.task_ids= 请问在 ruby 里如果隐含调用 self 的实例方法,是否局部变量赋值优先于 self 的 setter 方法?

是的,局部变量优先于方法查找...

#1 楼 @hooopo 在局部变量不存在的时候也是这样?

局部变量不存在的时候不会。 从你例子的情况看,是因为 db 属性字段都是通过 method missing 实现的,其实 task_ids=这个方法还没定义。

#3 楼 @hooopo 懂了.. 没仔细研究源码.

我也没看过源码。。我只是推测。。 你还是自己看一下吧:-)

这个跟方法的继承重写一样的把. 只要重写之后都会覆盖之前的. 局部变量也一样.

赋值方法一定要显示调用 self。ruby 看到赋值语句就会建立一个局部变量,即使没有实际运行

class A
  def name=(name)
    @name = name
  end

  def name
    "Class A"
  end

  def test_setter
    name = "A" 
    p name #=> "A"
    p @name #=> nil
  end

  def test_scope
    p name #=> "Class A"
    name = "A"  if false
    p name #=> nil
  end

end

#6 楼 @Ddl1st 不是这个意思 这里假设有一个方法 task_ids= 单并不存在局部变量 task_ids ,如果这样调用 task_ids= somevalue 应该是调用 task_ids=方法, 而不是创建这个不存在的局部变量

#5 楼 @hooopo 我去看过了,mongoid 虽然没用 method missing,不过用了 define_method 感觉可能也算 hook

不是一定要写 self 的, 例如

task_ids=([1,2,3])

#9 楼 @night_song 我觉得 define_method 似乎和直接定义 method 没区别阿.. 确定是 local variable 优先吗?

#10 楼 @aNdReW_Qx define_method 一般用于动态编程吧,研究 ruby 比研究 rails 有趣多了

class A
  attr_accessor :name
  def initialize name
    @name = name
  end
  def change_name
    change_me
  end
  private
  def change_me
    puts %(
           puts name #=> #{name}\n
           puts self.name #=> #{self.name}\n
           puts name=bar;self.name #=> #{name='bar';self.name}\n
           puts name #=> #{name}
           puts self.methods.eql?(methods) #=> #{self.methods.eql?(methods)}\n
           puts send(:name=,'bar');self.name #=> #{send(:name=,'bar');self.name}
           )
  end
end
a = A.new "foo"
a.change_name

输出结果

puts name #=> foo

puts self.name #=> foo

puts name=bar;self.name #=> foo

puts name #=> bar
puts self.methods.eql?(methods) #=> true

puts send(:name=,'bar');self.name #=> bar

可见 self 也被 'private'了

其实 task_id 读写是在 field :task_ids, type: Array ,:default => [] 定义的, 这里没必要用 method_missing ...

其实规则很简单 ... a = x 总是赋值语句而不是方法调用 a.b = x 总是方法调用

#7 楼 @john1king #13 楼 @night_song 实验下来确实如此, 感谢..搞懂一个隐藏的概念缺失

即使 setter 是 public method,也必须明确前缀 self,这是 ruby 中一个特例。 另外 setter 是 private 或 protected method 的话,很特殊,必须加 self。 the well-grounded rubyist p123. 有详细的介绍。 #14 楼 @aNdReW_Qx

需要 登录 后方可回复, 如果你还没有账号请 注册新账号