2013-12-01 2 views
7
class MyClass 
    def one 
    def two 
    end 
    end 
end 

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

Здесь я определяю метод два внутри другого метода один. Первый метод вызывается экземпляром MyClass (obj). Таким образом, self является obj, когда определяется метод two. когда я проверить владелец метода два его MyClassОпределения языка и метода Ruby

obj.instance_eval do 
    def three 
    end 
end 

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

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

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

+0

* + 1 * для очень хорошего исследования ... :) –

+0

thnks, когда я чувствую, что у меня есть дело с вещами, причуды, подобные этим, приходят и кусают меня :), сказав это, если вы определите методы в верхнем слое, эти методы становятся частными методами экземпляра Object, а не singleton-методами main (и я где-то читал, что это преднамеренный и только специальный случай) – orange

+0

ha, 'def self.two; end' будет вести себя как instance_eval – fl00r

ответ

2

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

+0

ic, поэтому, когда im определяет метод два, по умолчанию definee является классом self, и когда я делаю instance_eval, defaultdee является singleton-классом self ..... таким же образом, когда я определяю методы в toplevel, они становятся методами экземпляра объекта, поскольку по умолчанию задано значение Object, а не одноэлементный класс основного – orange

9

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

Пояснение:

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

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

Хорошо, это будет очищен, если вы посмотрите на линии ниже части D. Посмотрите от последнего, первого и второго line.Inside instance_eval блока, def third, вызывает third быть добавлен в качестве singleton_method объекта ob, с помощью вызова метода крюка BasicObject#singleton_method_added.

Это как MRI было написано.

+2

Это хороший метод трассировки Kernel # set_trace_func, я не знал об этом ... он ясно показывает, что произошло под капотом, но я до сих пор неясно, почему владение методом было другое, когда значение «я» было таким же? – orange

2

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, потому что вы явно открыли экземпляр только. Это нарушит инкапсуляцию, чтобы она работала так же, как и в другом случае.

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

Смежные вопросы