Почему собственный класс не эквивалентен self.class, когда он выглядит так похоже?
Я где-то пропустил записку, и я надеюсь, что вы мне это объясните.
Почему собственный класс объекта отличается от self.class
?
class Foo
def initialize(symbol)
eigenclass = class << self
self
end
eigenclass.class_eval do
attr_accessor symbol
end
end
end
Мой поезд логики, который приравнивает собственный класс с class.self
довольно просто:
class << self
это способ объявления методов класса, а не методов экземпляра. Это ярлык def Foo.bar
,
Так что в рамках ссылки на объект класса, возвращая self
должно быть идентично self.class
, Это потому что class << self
установил бы self
в Foo.class
для определения методов / атрибутов класса.
Я просто запутался? Или это хитрая уловка метапрограммирования в Ruby?
3 ответа
class << self
это больше, чем просто способ объявления методов класса (хотя его можно использовать и таким образом). Возможно, вы видели какое-то использование как:
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
Это работает и эквивалентно def Foo.a
, но способ, которым это работает, немного тонок. Секрет в том, что self
в этом контексте относится к объекту Foo
, чей класс является уникальным, анонимным подклассом Class
, Этот подкласс называется Foo
Собственный класс. Так def a
создает новый метод под названием a
в Foo
Собственный класс, доступный по обычному синтаксису вызова метода: Foo.a
,
Теперь давайте посмотрим на другой пример:
str = "abc"
other_str = "def"
class << str
def frob
return self + "d"
end
end
print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str
Этот пример такой же, как и предыдущий, хотя поначалу будет сложно сказать. frob
определяется, а не на String
класс, но на собственном классе str
, уникальный анонимный подкласс String
, Так str
имеет frob
метод, но случаи String
в общем нет. Мы также могли бы переопределить методы String (очень полезно в некоторых сложных тестовых сценариях).
Теперь мы готовы понять ваш оригинальный пример. внутри Foo
инициализировать метод, self
относится не к классу Foo
, но к какому-то конкретному случаю Foo
, Его собственный класс является подклассом Foo
, но это не так Foo
; это не могло быть, или иначе уловка, которую мы видели во втором примере, не могла работать. Итак, чтобы продолжить ваш пример:
f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)
f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.
Надеюсь это поможет.
Самый простой ответ: собственный класс не может быть создан.
class F
def eigen
class << self
self
end
end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
Иегуда Кац довольно хорошо объясняет тонкости в " Метапрограммировании в Ruby: все зависит от самого себя"