Ruby def и instance_eval vs. class_eval
Я читаю раздел "Метапрограммирование" в "Программирование Ruby 1.9", и мне трудно понять, что происходит внутри между class_eval
/class_exec
и instance_eval
/instance_exec
.
Итак, в первую очередь, я понимаю, что def
добавляет метод в таблицу методов self
(объект класса):
class A
puts self # => A
def foo; 42; end # added to the method table of self, so becomes an instance method
end
A.new.foo # => 42
И если мы используем class_eval
, мы получим то же поведение:
A.class_eval do
puts self # => A
def bar; 42; end # same as above
end
A.new.bar # => 42
Но как-то в случае instance_eval
все по-другому:
A.instance_eval do
puts self # => A
def baz; 42; end # added to the method table of an anonymous
# singleton class of self, so becomes a class method
end
puts A.baz # => 42
s = 'string'
s.instance_eval do ... end # same behavior, so now def creates an instance method
Итак, я понимаю функциональную разницу между class_eval
и instance_eval
.
Но контексты внутри блоков class_eval
и instance_eval
выглядят одинаково для меня - в частности, self
указывает на один и тот же объект, а local_variables
- то же самое. Итак, что происходит внутри блоков (внутренне), что делает def
действовать иначе?
Есть ли какая-то документация, которую я мог бы прочитать? RDoc для instance_eval и class_eval не помогает. Глядя на источник, instance_eval, кажется, настроил объект одноэлементного класса, тогда как class_eval нет - но разве это отличие видимо вне кода C, на уровне Ruby?
Ответы
Ответ 1
Я думаю, что ваша путаница исходит из того факта, что def не зависит от текущего "я", вы можете подумать об этом как о "текущем классе", который имеет свои собственные правила.
Следуя вашим примерам:
class A
# defs here go to A
puts self # => A
class << self
#defs here go to A eigenclass
end
end
A.class_eval do
#defs here go to A
end
A.instance_eval do
#defs here go to A eigenclass
end
s = "Hello World"
class << s
#defs here go to s eigenclass
end
Вот часть главы, в которой говорится о проблеме, и это довольно ясно о поведении
class_eval и instance_eval оба установлены self для продолжительности блока. Однако они отличаются тем, как они настроить среду для метода определение. class_eval устанавливает вещи вверх как будто вы были в теле класса, поэтому определения методов будут определить методы экземпляра. Напротив, вызов instance_eval в действиях класса как будто вы работали внутри одиночный класс. Следовательно, любые методы, которые вы определяете, станут класса.
Единственное, что, я думаю, стоит добавить, это то, что вы можете вызвать instance_eval в любом объекте, а не только в классах, и поведение не меняется, но имеет разные последствия.
Некоторое релевантное чтение:
Определения Ruby: instance_eval и class_eval
Глава 4 этой самой превосходной серии
Ответ 2
Просто добавьте в ответ @krusty.ar: def
и define_method
добавьте методы в контекст определения текущего метода (я считаю, что он вызвал, я не уверен), а не с текущим self
.
Это просто то, что внутри тела модуля, класса или Singleton эти два экземпляра совпадают.
Но, например, в теле script (aka top-level), self
является объектом main
, но контекст определения текущего метода Object
.