Почему не эквивалент eigenclass для класса self.class, когда он выглядит так похожим?

Я где-то пропустил записку, и надеюсь, вы объясните мне это.

Почему eigenclass объекта отличается от self.class?

class Foo
  def initialize(symbol)
    eigenclass = class << self
      self
    end
    eigenclass.class_eval do
      attr_accessor symbol
    end
  end
end

Моя логическая схема, которая приравнивает eigenclass к class.self, довольно проста:

class << self - это способ объявления методов класса, а не методов экземпляра. Это ярлык для def Foo.bar.

Итак, в ссылке на объект класса возвращение self должно быть идентично self.class. Это связано с тем, что class << self установил self в Foo.class для определения методов/атрибутов класса.

Я просто смущен? Или это хитроумный трюк мета-программирования Ruby?

Ответ 1

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 eigenclass. Таким образом, def a создает новый метод под названием a в Foo eigenclass, доступный с помощью синтаксиса нормального метода: 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 initialize self относится не к классу Foo, а к определенному экземпляру Foo. Его eigenclass является подклассом 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.

Надеюсь, что это поможет.

Ответ 2

Самый простой ответ: невозможно создать экземпляр eigenclass.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class