Dlaczego klasa własna nie jest odpowiednikiem self.class, skoro wygląda tak podobnie?

84

Gdzieś przegapiłem notatkę i mam nadzieję, że mi to wyjaśnisz.

Dlaczego klasa własna obiektu różni się od self.class?

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

Mój ciąg logiki, który utożsamia klasę własną z, class.selfjest raczej prosty:

class << selfjest sposobem deklarowania metod klasowych, a nie metod instancji. To jest skrót do def Foo.bar.

Zatem w ramach odwołania do obiektu klasy zwracanie selfpowinno być identyczne z self.class. Dzieje się tak, ponieważ class << selfzostałby ustawiony selfna Foo.classdla definicji metod / atrybutów klas.

Czy jestem po prostu zdezorientowany? A może jest to podstępna sztuczka metaprogramowania Rubiego?

Robert K.
źródło

Odpowiedzi:

123

class << selfjest czymś więcej niż tylko sposobem deklarowania metod klasowych (chociaż można tego używać w ten sposób). Prawdopodobnie widziałeś takie użycie, jak:

class Foo
  class << self
    def a
      print "I could also have been defined as def Foo.a."
    end
  end
end

To działa i jest równoważne def Foo.a, ale sposób, w jaki to działa, jest nieco subtelny. Sekret polega na tym self, że w tym kontekście odnosi się do obiektu Foo, którego klasa jest unikalną, anonimową podklasą klasy Class. Podklasa ta nazywana jest Foo„s eigenclass . Tak def atworzy nową metodę zwaną aw Foo„s eigenclass, dostępną przez normalną składnią Sposób połączenia: Foo.a.

Spójrzmy teraz na inny przykład:

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

Ten przykład jest taki sam jak poprzedni, chociaż na początku może być trudno powiedzieć. frobjest zdefiniowana nie w Stringklasie, ale w klasie własnej str, unikalnej anonimowej podklasie String. strMa więc frobmetodę, ale instancje Stringgeneralnie nie. Mogliśmy również przesłonić metody String (bardzo przydatne w niektórych trudnych scenariuszach testowania).

Teraz jesteśmy przygotowani, aby zrozumieć Twój oryginalny przykład. Wewnątrz Fooinicjuj metoda jest, selfnie odnosi się do grupy Foo, ale w pewnym konkretnym przypadku z Foo. Jego klasa własna jest podklasą Foo, ale tak nie jest Foo; to niemożliwe, bo inaczej sztuczka, którą widzieliśmy w drugim przykładzie, nie zadziałała. Kontynuując przykład:

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.

Mam nadzieję że to pomoże.

David Seiler
źródło
Zatem każda instancja jest anonimową podklasą utworzonej klasy?
Robert K
21
Klasa każdej instancji jest anonimową podklasą utworzonej klasy. Klasa f1 jest anonimową podklasą Foo, klasa Foo jest anonimową podklasą Class.
David Seiler
6
fajna odpowiedź :) wiele osób nie rozumie tego tak jasno jak Ty.
horseyguy
3
W jaki sposób klasa własna f1 różni się koncepcyjnie od rzeczywistego wystąpienia f1. Jeśli f1 jest jedyną instancją, która kiedykolwiek będzie miała dostęp do metod swojej własnej klasy, czy rozróżnienie między f1 a jego własną klasą nie załamie się?
elju,
1
@elju Yeah, trochę. Naprawdę ważne rozróżnienie dotyczy „Foo” i „klasy własnej f1”; jeśli to masz, prawdopodobnie wszystko w porządku.
David Seiler,
48

Najprostsza odpowiedź: nie można utworzyć instancji klasy własnej.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
b.vandgrift
źródło
możesz mieć tylko 1 punkt na tej stronie, ale lubię Ciebie i Twój styl.
horseyguy
Zgadzam się z poręczą; to świetna odpowiedź
Christopher Scott,
3
To niezwykle wnikliwy i pomocny komentarz IFF, który przeczytałem już powyżej, odpowiedź @ DavidSeiler.
Jazz,
Moc Theo tutaj demonstruje wyjątek, który został zgłoszony.
Nowa Aleksandria