Pregunta

Python tiene la idea de metaclassas que, si lo entiendo correctamente, le permiten modificar un objeto de una clase en el momento de la construcción. No está modificando la clase, sino el objeto que se creará y luego se inicializa.

Python (al menos a 3.0, creo) también tiene la idea de los decoradores de clase. Nuevamente, si lo entiendo correctamente, los decoradores de clase permiten la modificación de la definición de clase en el momento en que se está declarando.

Ahora creo que hay una característica o características equivalentes para el decorador de clases en Ruby, pero actualmente desconoce algo equivalente a las metaclassas. Estoy seguro de que puede bombear fácilmente cualquier objeto Ruby a través de algunas funciones y hacer lo que quiere, pero ¿hay una característica en el lenguaje que establece que lo hacen las metaclassas?

Entonces, de nuevo, ¿Ruby tiene algo similar a las metaclassas de Python?

Editar Estaba en las metaclasas de Python. Aparece un metaclase y un decorador de clase muy similares. Ambos modifican la clase cuando se define pero en diferentes modales. Esperemos que un gurú de Python entre y explique mejor en estas características en Python.

Pero una clase o el padre de una clase puede implementar un __new__(cls[,..]) función que personaliza la construcción del objeto antes de que se inicialice con __init__(self[,..]).

Editar Esta pregunta es principalmente para la discusión y el aprendizaje sobre cómo se comparan los dos idiomas en estas características. Estoy familiarizado con Python pero no Ruby y tenía curiosidad. Esperemos que cualquier otra persona que tenga la misma pregunta sobre los dos idiomas encuentre esta publicación útil y esclarecedora.

¿Fue útil?

Solución

Ruby no tiene metaclase. Hay algunas construcciones en Ruby que algunas personas a veces llaman metaclasses erróneamente, pero no lo son (lo cual es una fuente de sin fin confusión).

Sin embargo, hay muchas maneras de lograr los mismos resultados en Ruby que haría con metaclase. Pero sin decirnos qué es exactamente lo que quieres hacer, no se sabe cuáles podrían ser esos mecanismos.

En breve:

  • Ruby no tiene metaclase
  • Ruby no tiene ninguna construcción que corresponda a las metaclasas de Python
  • Todo lo que Python puede hacer con metaclasses también se puede hacer en Ruby
  • Pero no hay único construir, utilizará diferentes construcciones dependiendo de lo que desee exactamente
  • Cualquiera de esas construcciones probablemente también tenga otras características que no correspondan a las metaclasas (aunque probablemente corresponden a algo más En Python)
  • Si bien puedes hacer cualquier cosa en Ruby que puedas hacer con metaclasas en Python, no necesariamente será sencillo
  • Aunque a menudo habrá una solución más rubí que es elegante
  • Por último, pero no menos importante: si bien puedes hacer cualquier cosa en Ruby que puedas hacer con metaclasas en Python, hacerlo podría no ser necesariamente la forma de Ruby

Y qué son ¿Metaclasses exactamente? Bueno, son clases de clases. Entonces, retrocedamos: ¿Qué son clases ¿exactamente?

Clases ...

  • son fábricas para objetos
  • Definir el comportamiento de los objetos
  • Definir a nivel metafísico lo que significa ser una instancia de la clase

Por ejemplo, el Array La clase produce objetos de matriz, define el comportamiento de las matrices y define lo que significa "matriz".

Volver a Metaclasses.

Metaclasas ...

  • son fábricas para clases
  • Defina el comportamiento de las clases
  • Definir a nivel metafísico lo que significa ser una clase

En Ruby, esas tres responsabilidades se dividen en tres lugares diferentes:

  • la Class La clase crea clases y define un poco del comportamiento
  • El propina de la clase individual define un poco del comportamiento de la clase
  • El concepto de "clase" está conectado al intérprete, que también implementa la mayor parte del comportamiento (por ejemplo, no puede heredar desde Class Para crear un nuevo tipo de clase que busque métodos de manera diferente, o algo así, el algoritmo de búsqueda del método está conectado al intérprete)

Entonces, esas tres cosas juntas juegan el papel de las metaclassas, pero ninguno de ellos es un metaclase (cada uno solo implementa una pequeña parte de lo que hace una metaclase), ni el es el suma de ellos el metaclase (porque hacen mucho más que eso).

Desafortunadamente, algunas personas llaman a Eigenclasses de clases metaclasses. (Hasta hace poco, era una de esas almas equivocadas, hasta que finalmente vi la luz). Otras personas llaman todos Eigenclasses Metaclasses. (Desafortunadamente, una de esas personas es el autor de uno de los tutoriales más populares sobre la metaprograma de Ruby y el modelo de objetos Ruby). Algunas bibliotecas populares agregan un metaclass método a Object Eso devuelve el eigenclass del objeto (por ejemplo, ActiveSupport, Facets, Metaid). Algunas personas llaman todos Clases virtuales (es decir, Eigenclasses e incluyen clases) Metaclasses. Algunas personas llaman Class el metaclase. Incluso dentro del código fuente de Ruby en sí, la palabra "metaclase" se usa para referirse a cosas que no son metaclase.

Otros consejos

Su pregunta actualizada se ve bastante diferente ahora. Si lo entiendo correctamente, desea conectarse a la asignación de objetos y la inicialización, que no tiene absolutamente nada que ver con las metaclassas. (Pero tu todavía No escribas lo que realmente quieres hacer, así que aún podría estar fuera).

En algunos idiomas orientados a objetos, los objetos son creados por constructores. Sin embargo, Ruby no tiene constructores. Los constructores son solo métodos de fábrica (con estúpidas restricciones); No hay razón para tenerlos en un lenguaje bien diseñado, si puede usar un método de fábrica (más potente) en su lugar.

La construcción de objetos en Ruby funciona como este: la construcción de objetos se divide en dos fases, asignación y inicialización. La asignación se realiza por un método de clase pública llamado allocate, que se define como un método de instancia de clase Class y es generalmente nunca anulación. (De hecho, no creo que realmente pueden anularlo.) Simplemente asigna el espacio de memoria para el objeto y establece algunos punteros, sin embargo, el objeto no se puede usar en este momento.

Ahí es donde entra el inicializador: es un método de instancia llamado initialize, que establece el estado interno del objeto y lo lleva a un estado consistente y totalmente definido que puede ser utilizado por otros objetos.

Entonces, para crear completamente un nuevo objeto, lo que necesita hacer es esto:

x = X.allocate
x.initialize

Nota: los programadores de Objective-C pueden reconocer esto.

Sin embargo, porque es demasiado fácil olvidar llamar initialize y como regla general, un objeto debe ser completamente válido después de la construcción, se llamó un método de fábrica de conveniencia llamado Class#new, que hace todo lo que funciona para ti y se ve algo así:

class Class
  def new(*args, &block)
    obj = allocate
    obj.initialize(*args, &block)

    return obj
  end
end

Nota: en realidad, initialize es privado, por lo que la reflexión debe usarse para eludir las restricciones de acceso como esta: obj.send(:initialize, *args, &block)]

Esa, por cierto, es la razón por la cual construir un objeto llamar Un método de clase pública Foo.new pero tu implementar Un método de instancia privada Foo#initialize, que parece tropezar con muchos recién llegados.

Sin embargo, ninguna de esto está de alguna manera horneado en el idioma. El hecho de que el método de fábrica principal para cualquier clase generalmente se llama new es solo una convención (y a veces deseo que fuera diferente, porque se parece a los constructores en Java, pero es completamente diferente). En otros idiomas, el constructor debe tener un nombre específico. En Java, debe tener el mismo nombre que la clase, lo que significa que a) solo puede haber un constructor yb) las clases anónimas no pueden tener constructores porque no tienen nombres. En Python, se debe llamar al método de fábrica __new__, que nuevamente significa que solo puede haber uno. (Tanto en Java como en Python, por supuesto, puede tener diferentes métodos de fábrica, pero llamarlos se ve diferente de llamar al valor predeterminado, mientras que en Ruby (y SmallTalk de donde se originó este patrón) se ve igual)).

En Ruby, puede haber tantos métodos de fábrica como desee, con cualquier nombre que desee, y un método de fábrica puede tener muchos nombres diferentes. (Para las clases de recolección, por ejemplo, el método de fábrica a menudo está alias a [], que te permite escribir List[1, 2, 3] en vez de List.new(1, 2, 3) que termina luciendo más como una matriz, enfatizando así la naturaleza de la colección de las listas).

En breve:

  • El método de fábrica estandarizado es Foo.new, pero puede ser cualquier cosa
  • Foo.new llamadas allocate para asignar memoria para un objeto vacío foo
  • Foo.new luego llamadas foo.initialize, es decir, el Foo#initialize método de instancia
  • Los tres son solo métodos como cualquier otro, que puede inventar, redefinir, anular, envolver, alias y otras cosas.
  • Bueno, excepto allocate que necesita asignar memoria dentro del tiempo de ejecución de Ruby que realmente no puede hacer con Ruby

En Python, __new__ corresponde aproximadamente a ambos new y allocate en Ruby, y __init__ exactamente corresponde a initialize En Ruby. La principal diferencia es que en Ruby, new llamadas initialize mientras que en Python, el tiempo de ejecución Llama automáticamente __init__ después __new__.

Por ejemplo, aquí hay una clase que solo permite un máximo de 2 instancias creadas:

class Foo
  def self.new(*args, &block)
    @instances ||= 0
    raise 'Too many instances!' if @instances >= 2

    obj = allocate
    obj.send(:initialize, *args, &block)

    @instances += 1

    return obj
  end

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

one = Foo.new('#1')
two = Foo.new('#2')
puts two.name         # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top