Почему собственный класс не эквивалентен 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: все зависит от самого себя"

Другие вопросы по тегам