Ruby self и определения методов

class MyClass
  def one
    def two
    end
  end
end

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

здесь я определяю два метода внутри другого метода. Метод one вызывается экземпляром MyClass (obj) . Поэтому самостоятельно это obj когда способ определена. когда я проверяю владельца метода два, это MyClass

obj.instance_eval do
  def three
  end
end

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

в этом фрагменте я делаю instance_eval на obj, поэтому self снова obj, когда определен метод three . Но когда я проверяю владельца трех, это одноэлементный класс obj

почему это? есть ли что-то еще, кроме себя? что определяет, куда идет определение метода ??

3 ответов


это объясняется в хорошей статье Ruby-core contributor yugui:три неявных контекста в Ruby. В принципе, существует контекст определения по умолчанию, который является не аналогично self. Методы, которые явно не определены как одноэлементные методы, заканчиваются как методы экземпляра контекста определения по умолчанию. module и class тела определений изменяют контекст определения по умолчанию, тогда как def - нет. instance_eval OTOH тут изменить его.


я хотел бы использовать 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]

объяснение:

посмотреть 5 строки ниже часть. Он просто говорит нам, когда Ruby найдет def ключевое слово внутри класса, он добавит этот метод в качестве метода экземпляра к этот класс. Это делается путем вызова метода hook Module#method_added. То же самое объяснение идет на две строки ниже часть C.

теперь, что происходит внутри obj.instance_eval {..} ?

Ok, это будет очищено, если вы посмотрите на строки ниже часть D. Смотри с последнего,первый и второй линии.Внутри instance_eval блок, def third причины third добавить в качестве singleton_method объекта ob, вызывая метод hook BasicObject#singleton_method_added.

это как МРТ было написано.


def это не метод, поэтому его не нужно вести себя как метод, когда дело доходит до self. Это сбивает с толку, потому что эти два, очевидно, эквивалентны:

class Foo
  def one
    "one"
  end

  define_method(:two) { "two" }
end

пока эти двое явно не (экземпляров бара нет define_method)

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

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

вы можете просмотреть это как аргумент для того, почему вложенные def принадлежит класс. Это имеет смысл, потому что этот вид вложенных def и только можно когда вы открыли область класса, поэтому она влияет на каждый экземпляр.

С другой стороны, это определенно имеет смысл, что def на instance_eval добавляется в класс singleton, потому что вы явно открыли только экземпляр. Это нарушило бы инкапсуляцию, чтобы она работала так же, как и в другом случае.

так... в основном его исключительное поведение. Но это не совсем бессмысленно.