Pourquoi la classe propre n'est-elle pas équivalente à self.class, alors qu'elle a l'air si semblable?
-
06-07-2019 - |
Question
J'ai oublié le mémo quelque part et j'espère que vous me l'expliquerez.
Pourquoi la classe propre d'un objet est-elle différente de self.class
?
class Foo
def initialize(symbol)
eigenclass = class << self
self
end
eigenclass.class_eval do
attr_accessor symbol
end
end
end
Mon train de logique qui assimile la classe propre à class.self
est plutôt simple:
class << self
est un moyen de déclarer des méthodes de classe plutôt que des méthodes d'instance. C'est un raccourci vers def Foo.bar
.
Ainsi, dans la référence à l'objet de classe, le renvoi self
doit être identique à Foo.class
. En effet, <=> définirait <=> sur <=> pour la définition des méthodes / attributs de classe.
Suis-je simplement confus? Ou est-ce un truc sournois de la méta-programmation Ruby?
La solution
class << self
est plus qu'un simple moyen de déclarer des méthodes de classe (bien qu'il puisse être utilisé de cette façon). Vous avez probablement déjà vu des utilisations telles que:
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
Cela fonctionne et équivaut à def Foo.a
, mais la façon dont cela fonctionne est un peu subtile. Le secret est que self
, dans ce contexte, fait référence à l'objet Foo
, dont la classe est une sous-classe unique et anonyme de Class
. Cette sous-classe est appelée def a
eigenclass . Ainsi, a
crée une nouvelle méthode appelée Foo.a
dans la classe propre de frob
, accessible par la syntaxe d'appel de méthode habituelle: String
.
Maintenant, regardons un exemple différent:
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
Cet exemple est le même que le dernier, mais il peut être difficile de le savoir au début. str
est défini, pas sur la classe <=>, mais sur la classe propre de <=>, une sous-classe anonyme unique de <=>. Donc <=> a une méthode <=>, mais pas les instances de <=> en général. Nous pourrions également avoir remplacé les méthodes de String (très utile dans certains scénarios de test difficiles).
Nous sommes maintenant en mesure de comprendre votre exemple initial. Dans la méthode d'initialisation de <=>, <=> ne fait pas référence à la classe <=>, mais à une instance particulière de <=>. Sa classe propre est une sous-classe de <=>, mais ce n'est pas <=>; cela ne pouvait pas être, sinon le truc que nous avons vu dans le deuxième exemple ne pourrait pas marcher. Donc, pour continuer votre exemple:
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.
J'espère que cela vous aidera.
Autres conseils
La réponse la plus simple: la classe propre ne peut pas être instanciée.
class F
def eigen
class << self
self
end
end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
Yehuda Katz explique très bien les subtilités de & "; Métaprogrammation en Ruby: tout est une question de soi &";