【问题标题】:Ruby self and method definitionsRuby 自我和方法定义
【发布时间】:2013-12-01 11:43:28
【问题描述】:
class MyClass
  def one
    def two
    end
  end
end

obj = MyClass.new
obj.one
puts obj.method(:two).owner  #==> MyClass

在这里,我在另一个方法一中定义了方法二。方法一由 MyClass (obj) 的实例调用。所以定义方法二的时候self就是obj。当我检查方法二的所有者时,它是 MyClass

obj.instance_eval do
  def three
  end
end

puts obj.method(:three).owner  #==> #<Class:#<MyClass:0x007f85db109010>>

在这个 sn-p 中,我在 obj 上做 instance_eval,所以当方法三被定义时,self 又是 obj。但是当我检查三个的所有者时,它是 obj 的单例类

这是为什么?除了 self 之外,还有什么其他东西决定了方法定义的去向吗??

【问题讨论】:

  • +1 进行非常好的调查... :)
  • thnks ,每当我觉得我已经掌握了一些事情时,像这样的怪癖就会来咬我 :) 话虽如此,如果你在顶层定义方法,这些方法将成为 Object 的私有实例方法和不是 main 的单例方法(我在某处读到这是故意的,而且只是特殊情况)
  • 哈哈,def self.two; end 的行为类似于 instance_eval

标签: ruby self


【解决方案1】:

我将使用Kernel#set_trace_func 方法向您解释幕后发生的事情。先看下面的代码和输出:

trace = lambda do |event,file,line,id,binding,klass|
    p [event,File.basename(file),line,id,binding,klass]
end


set_trace_func trace

class MyClass
  def self.bar;end
  def one
    def two
    end
  end
end

obj = MyClass.new
obj.one
obj.instance_eval do
  def three
  end
end

输出:

-----------------
----------------
-----------------
-----------------
-----------------
----------------- # part A
["c-call", "test.rb", 9, :singleton_method_added, #<Binding:0x83ab2b0>, BasicObject]
["c-return", "test.rb", 9, :singleton_method_added, #<Binding:0x83aaeb4>, BasicObject]
["line", "test.rb", 10, nil, #<Binding:0x83aab80>, nil]
["c-call", "test.rb", 10, :method_added, #<Binding:0x83aa900>, Module]
["c-return", "test.rb", 10, :method_added, #<Binding:0x83aa07c>, Module]
----------------------------- # part B
["line", "test.rb", 16, nil, #<Binding:0x83a976c>, nil]
["c-call", "test.rb", 16, :new, #<Binding:0x83a9488>, Class]
["c-call", "test.rb", 16, :initialize, #<Binding:0x83a90a0>, BasicObject]
["c-return", "test.rb", 16, :initialize, #<Binding:0x83a8e20>, BasicObject]
["c-return", "test.rb", 16, :new, #<Binding:0x83a8b28>, Class]
---------------------------
---------------------------
--------------------------- # part C
["c-call", "test.rb", 11, :method_added, #<Binding:0x83a7de0>, Module]
["c-return", "test.rb", 11, :method_added, #<Binding:0x83a79f8>, Module]
--------------------------- # part D
["line", "test.rb", 18, nil, #<Binding:0x83a7034>, nil]
["c-call", "test.rb", 18, :instance_eval, #<Binding:0x83a6c10>, BasicObject]
["line", "test.rb", 19, nil, #<Binding:0x83a65f8>, nil]
["c-call", "test.rb", 19, :singleton_method_added, #<Binding:0x83a61d4>, BasicObject]
["c-return", "test.rb", 19, :singleton_method_added, #<Binding:0x83a5ef0>, BasicObject]
["c-return", "test.rb", 18, :instance_eval, #<Binding:0x83a5d4c>, BasicObject]

说明:

看看A部分下面的5行。它只是告诉我们 Ruby 何时会在一个类中找到 def 关键字,它会将该方法作为实例方法添加到该类中。这是通过调用钩子方法Module#method_added 来完成的。 part C下面的两行也有同样的解释。

现在obj.instance_eval {..} 内部发生了什么?

好的,如果您查看 part D 下面的行,这将被清除。从最后一行看,firstsecond 行。在 instance_eval 块内,def third 导致 third 添加为对象 @ 的 singleton_method 987654334@,通过调用钩子方法BasicObject#singleton_method_added

MRI 就是这样写的。

【讨论】:

  • 这是很好的跟踪方法 Kernel#set_trace_func ,我不知道它......它确实清楚地说明了引擎盖下发生了什么,但我仍然不清楚为什么方法的所有权当self的值相同时会不同吗?
【解决方案2】:

ruby-core 贡献者 yugui 在一篇不错的文章中对此进行了解释:Three implicit contexts in Ruby。基本上,有一个默认的定义上下文,它self相同。未明确定义为单例方法的方法最终会成为默认定义上下文的实例方法。 moduleclass 定义主体会更改默认定义上下文,而 def 不会。 instance_evalOTOH 确实改变了它。

【讨论】:

  • ic,所以当我定义方法二时,默认的definee是self的类,当我做instance_eval时,默认definee是self的singleton类.....当我定义的时候顶层的方法它们成为 Object 的实例方法,因为默认定义是 Object 而不是 main 的单例类
【解决方案3】:

def 不是一个方法,所以它在涉及self 时不需要表现得像一个方法。这很令人困惑,因为这两个显然是等价的:

class Foo
  def one
    "one"
  end

  define_method(:two) { "two" }
end

虽然这两个显然不是(Bar 的实例没有define_method

class Bar
  def one
    def two
      "two"
    end
    "one"
  end

  def three
    define_method(:four) { "four" }
    "three"
  end
end

您可以将此视为嵌套def 归类所有的一个论据。这是有道理的,因为这种嵌套的def只有在您打开类范围时才可能,因此它会影响每个实例。

另一方面,将instance_eval 中的def 添加到单例类中绝对是有意义的,因为您只显式地打开了实例。它会破坏封装,使其与其他情况相同。

所以...基本上它的异常行为。但这并不完全是荒谬的。

【讨论】:

    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 2012-10-21
    • 2023-04-01
    • 2014-08-25
    • 1970-01-01
    • 2010-09-19
    相关资源
    最近更新 更多