¿Por qué la clase propia no es equivalente a self.class, cuando se ve tan similar?


83

Me perdí el memo en alguna parte y espero que me lo explique.

¿Por qué es diferente la clase propia 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 equipara la clase propia con class.selfes bastante simple:

class << selfes una forma de declarar métodos de clase, en lugar de métodos de instancia. Es un atajo a def Foo.bar.

Entonces, dentro de la referencia al objeto de clase, la devolución selfdebe ser idéntica a self.class. Esto se debe a que class << selfse establecería selfen Foo.classpara la definición de métodos / atributos de clase.

¿Estoy confundido? ¿O es un truco furtivo de la metaprogramación de Ruby?

Respuestas:


122

class << selfes más que una forma de declarar métodos de clase (aunque se puede usar de esa manera). Probablemente hayas visto algún 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 Foo's eigenclass . Así def ase crea un nuevo método llamado aen Fooeigenclass 's, con acceso por la sintaxis de llamada 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 saberlo al principio. frobse define, no en la Stringclase, sino en la clase propia de str, una subclase anónima única de String. También strtiene un frobmétodo, pero las instancias de Stringen general no. También podríamos tener métodos anulados de String (muy útiles en ciertos escenarios de prueba complicados).

Ahora estamos equipados para comprender su ejemplo original. FooEl método initialize de Inside , selfno se refiere a la clase Foo, sino a alguna instancia particular 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 con 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.


Entonces, ¿cada instancia es una subclase anónima de la clase creada?
Robert K

21
La clase de cada instancia es una subclase anónima de la clase creada. La clase de f1 es una subclase anónima de Foo, la clase de Foo es una subclase anónima de Class.
David Seiler

6
buena respuesta :) mucha gente no entiende esto tan claramente como tú.
Horseyguy

3
¿En qué se diferencia la clase propia de f1 entonces, conceptualmente, de la instancia real de f1? Si f1 es la única instancia que alguna vez tendrá acceso a los métodos de su clase propia, ¿no se rompe la distinción entre f1 y su clase propia?
elju

1
@elju Sí, un poco. La distinción realmente importante es entre "Foo" y "clase propia de f1"; si tienes eso, probablemente estés bien.
David Seiler

46

La respuesta más simple: no se puede crear una instancia de la clase propia.

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

Puede que solo tengas 1 punto en este sitio web, pero me gustas y tu estilo.
Horseyguy

De acuerdo con barandilla; esta es una gran respuesta
Christopher Scott

3
Este es un comentario extremadamente perspicaz y útil. IFF ya ha leído la respuesta de @ DavidSeiler anterior.
Jazz

Theo power aquí está demostrando la excepción que se plantea.
Nueva Alejandría

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.