ruby - ¿Por qué la clase de auto equivalente no es equivalente a self.class, cuando parece tan similar?
language-features eigenclass (3)
La respuesta más simple: la clase de genes no puede ser instanciada.
class F
def eigen
class << self
self
end
end
end
F.new.eigen.new #=> TypeError: can''t create instance of virtual class
Me he perdido la nota en alguna parte, y espero que me lo expliques.
¿Por qué la clase de auto de un objeto es diferente de self.class
?
class Foo
def initialize(symbol)
eigenclass = class << self
self
end
eigenclass.class_eval do
attr_accessor symbol
end
end
end
Mi tren de lógica que iguala la clase propia con class.self
es bastante simple:
class << self
es una forma de declarar métodos de clase, en lugar de métodos de instancia. Es un atajo para def Foo.bar
. def Foo.bar
.
Entonces, dentro de la referencia al objeto de la clase, el self
regresa debe ser idéntico a self.class
. Esto se debe a que class << self
se establecería a Foo.class
para la definición de los métodos / atributos de clase.
¿Estoy confundido? O, ¿es este un truco furtivo de la meta-programación de Ruby?
Yehuda Katz hace un buen trabajo explicando las sutilezas en " Metaprogramación en Ruby: todo es sobre uno mismo "
class << self
es más que una forma de declarar métodos de clase (aunque se puede usar de esa manera). Probablemente has visto un uso como:
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
Esto funciona, y es equivalente a def Foo.a
, pero la forma en que funciona es un poco sutil. El secreto es que self
, en ese contexto, se refiere al objeto Foo
, cuya clase es una subclase única y anónima de Class
. Esta subclase se llama eigenclass de Foo
. Así que def a
crea un nuevo método llamado a
en la clase propia de Foo
, accesible mediante la sintaxis de llamada al método normal: Foo.a
Ahora veamos un ejemplo diferente:
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
Este ejemplo es el mismo que el anterior, aunque puede ser difícil de decir al principio. frob
se define, no en la clase String
, sino en la clase propia de str
, una subclase anónima única de String
. Entonces str
tiene un método frob
, pero las instancias de String
en general no. También podríamos haber reemplazado los métodos de String (muy útil en ciertos escenarios difíciles de prueba).
Ahora estamos equipados para comprender su ejemplo original. Dentro del método de inicialización de Foo
, el self
no se refiere a la clase Foo
, sino a alguna instancia particular de Foo
. Su clase propia es una subclase de Foo
, pero no es Foo
; no podría ser, o el truco que vimos en el segundo ejemplo no podría funcionar. Entonces para continuar tu ejemplo:
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.
Espero que esto ayude.