Pregunta ¿Por qué la clase de auto equivalente no es equivalente a self.class, cuando parece tan similar?


Me he perdido la nota en alguna parte, y espero que me lo expliques.

¿Por qué es distinta la clase de objeto de un objeto? 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 de genes 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.

Entonces, dentro de la referencia al objeto de la clase, regresando self debe ser idéntico a self.class. Esto es porque class << self establecería self 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?


76
2017-10-27 13:31


origen


Respuestas:


class << self es más que solo 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 Fooes eigenclass. Asi que def a crea un nuevo método llamado a en Foo's eigenclass, accesible por 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 está definido, no en el String clase, pero en la clase de str, una subclase anónima única de String. Asi que str tiene un frob método, pero 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 FooMétodo de inicialización de self no se refiere a la clase Foo, pero a algunos particulares ejemplo de Foo. Su clase propia es una subclase de Foo, pero no lo 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.


114
2017-10-27 13:59



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

44
2017-11-16 16:09



Yehuda Katz hace un buen trabajo al explicar las sutilezas en "Metaprogramación en Ruby: se trata de uno mismo"


11
2017-12-21 01:38