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


84

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

Почему собственный класс объекта отличается от 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?

Ответы:


123

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«s eigenclass . Таким образом 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.

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


Итак, каждый экземпляр является анонимным подклассом созданного класса?
— Роберт К.

21
Класс каждого экземпляра является анонимным подклассом созданного класса. Класс f1 является анонимным подклассом Foo, класс Foo является анонимным подклассом Class.
— Дэвид Сейлер,

6
хороший ответ :) многие люди не понимают этого так ясно, как вы.
— Horseyguy

3
Чем собственный класс f1 концептуально отличается от фактического экземпляра f1. Если f1 - единственный экземпляр, который когда-либо будет иметь доступ к методам своего собственного класса, разве не нарушается различие между f1 и его собственным классом?
— elju

1
@elju Да, вроде как. Действительно важное различие - между «Foo» и «собственным классом f1»; если у вас есть это, вы, вероятно, в порядке.
— Дэвид Зайлер

48

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

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

у вас может быть только 1 балл на этом сайте, но вы мне нравитесь и ваш стиль.
— Horseyguy

Согласен с перилами; это отличный ответ
— Кристофер Скотт

3
Это чрезвычайно проницательный и полезный комментарий. IFF уже прочитал ответ @ DavidSeiler выше.
— Джаз

Тео Пауэр здесь демонстрирует возникшее исключение.
— Новая Александрия

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.