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