En programación orientada a objetos , una metaclase es una clase cuyas instancias son clases en sí mismas. A diferencia de las clases ordinarias, que definen el comportamiento de los objetos, las metaclases especifican el comportamiento de las clases y sus instancias. No todos los lenguajes de programación orientados a objetos admiten el concepto de metaclases. Para aquellos que lo hacen, el grado de control que las metaclases tienen sobre los comportamientos de clase varía. Las metaclases a menudo se implementan tratando a las clases como ciudadanos de primera clase , haciendo de una metaclase un objeto que crea y administra estas clases. Cada lenguaje de programación se adhiere a su propio protocolo de metaobjetos , que son las reglas que determinan las interacciones entre objetos, clases y metaclases. [1] Las metaclases se utilizan para automatizar la generación de código y mejorar el desarrollo del marco. [2]
En Python , la clase incorporada type
es una metaclase. [3] [4] [5] Considere esta sencilla clase de Python:
clase Coche : def __init__ ( self , marca : str , modelo : str , año : int , color : str ): self . hacer = hacerse uno mismo . modelo = modelo de sí mismo . año = año yo . color = color @property def descripción ( self ) -> str : """Devolver una descripción de este automóvil.""" return f " { self . color } { self . make } { self . model } "
En tiempo de ejecución, Car
él mismo es una instancia de type
. El código fuente de la Car
clase, mostrado arriba, no incluye detalles como el tamaño en bytes de los Car
objetos, su disposición binaria en la memoria, cómo se asignan, que el __init__
método se llama automáticamente cada vez que Car
se crea a, etc. Estos detalles entran en juego no sólo cuando Car
se crea un nuevo objeto, sino también cada vez que Car
se accede a cualquier atributo de a. En lenguajes sin metaclases, estos detalles están definidos por la especificación del lenguaje y no se pueden anular. En Python, la metaclase type
controla estos detalles del Car
comportamiento. Se pueden anular utilizando una metaclase diferente en lugar de type
.
El ejemplo anterior contiene código redundante relacionado con los cuatro atributos make
, model
, year
y color
. Es posible eliminar parte de esta redundancia utilizando una metaclase personalizada. En Python, una metaclase se define más fácilmente como una subclase de type
.
clase AttributeInitType ( tipo ): def __call__ ( self , * args , ** kwargs ): "" "Crear una nueva instancia.""" # Primero, cree el objeto de la forma predeterminada normal. obj = tipo . __llamar__ ( yo , * argumentos ) # Además, establezca atributos en el nuevo objeto. para nombre , valor en kwargs . elementos (): setattr ( obj , nombre , valor ) # Devolver el nuevo objeto. objeto de retorno
Esta metaclase solo anula la creación de objetos. Todos los demás aspectos del comportamiento de clases y objetos todavía son manejados por type
.
Ahora la clase Car
se puede reescribir para usar esta metaclase. En Python 3 esto se hace proporcionando un "argumento de palabra clave" metaclass
a la definición de clase:
clase Coche ( objeto , metaclase = AttributeInitType ): @property def descripción ( self ) -> str : """Devuelve una descripción de este coche.""" return " " . unirse ( str ( valor ) para valor en self . __dict__ . valores ())
Se puede crear una instancia del objeto resultante Car
como de costumbre, pero puede contener cualquier número de argumentos de palabras clave:
new_car = Coche ( marca = 'Toyota' , modelo = 'Prius' , año = 2005 , color = 'Verde' , motor = 'Híbrido' )
En Smalltalk , todo es un objeto . Además, Smalltalk es un sistema basado en clases , lo que significa que cada objeto tiene una clase que define la estructura de ese objeto (es decir, las variables de instancia que tiene el objeto) y los mensajes que entiende un objeto. En conjunto, esto implica que una clase en Smalltalk es un objeto y que, por lo tanto, una clase debe ser una instancia de una clase (llamada metaclase).
Por ejemplo, un objeto automóvil c
es una instancia de la clase Car
. A su vez, la clase Car
vuelve a ser un objeto y, como tal, una instancia de la metaclase de Car
call Car class
. Tenga en cuenta el espacio en blanco en el nombre de la metaclase. El nombre de la metaclase es la expresión de Smalltalk que, cuando se evalúa, da como resultado el objeto de metaclase. Por lo tanto, la evaluación Car class
da como resultado el objeto de metaclase Car
cuyo nombre es Car class
(se puede confirmar esto evaluando Car class name
cuál devuelve el nombre de la metaclase de Car
).
Los métodos de clase en realidad pertenecen a la metaclase, al igual que los métodos de instancia pertenecen en realidad a la clase. Cuando se envía un mensaje al objeto 2
, la búsqueda del método comienza en Integer
. Si no se encuentra, continúa hacia arriba en la cadena de superclases, deteniéndose en el Objeto, ya sea que se encuentre o no.
Cuando se envía un mensaje a Integer
la búsqueda, el método comienza en Integer class
y continúa por la cadena de superclase hasta Object class
. Tenga en cuenta que, hasta ahora, la cadena de herencia de metaclases sigue exactamente a la cadena de herencia de clases. Pero la cadena de metaclases se extiende más allá porque Object class
es la subclase de Class
. Todas las metaclases son subclases de Class.
Al principio de Smalltalks, solo había una metaclase llamada Class
. Esto implicaba que los métodos que tenían todas las clases eran los mismos, en particular el método para crear nuevos objetos, es decir, new
. Para permitir que las clases tengan sus propios métodos y sus propias variables de instancia (llamadas variables de instancia de clase y no deben confundirse con variables de clase ), Smalltalk-80 introdujo para cada clase C
su propia metaclase C class
. Esto significa que cada metaclase es efectivamente una clase singleton .
Dado que no existe ningún requisito de que las metaclases se comporten de manera diferente entre sí, todas las metaclases son instancias de una sola clase llamada Metaclass
. Metaclass
Se llama a la metaclase de, Metaclass class
que nuevamente es una instancia de clase Metaclass
.
En Smalltalk-80, cada clase (excepto Object
) tiene una superclase . La superclase abstracta de todas las metaclases es Class
, que describe la naturaleza general de las clases.
La jerarquía de superclases para las metaclases es paralela a la de las clases, excepto para class Object
. TODAS las metaclases son subclases de Class
, por lo tanto:
Object class superclass == Class.
Al igual que los gemelos siameses , las clases y metaclases nacen juntas. Metaclass
tiene una variable de instancia thisClass
, que apunta a su clase conjunta. Tenga en cuenta que el navegador de clases Smalltalk habitual no muestra las metaclases como clases separadas. En cambio, el navegador de clases permite editar la clase junto con su metaclase al mismo tiempo.
Los nombres de las clases en la jerarquía de metaclases se confunden fácilmente con los conceptos del mismo nombre. Por ejemplo:
Object
es la clase base que proporciona métodos comunes para todos los objetos; "un objeto" es un número entero, un widget, Car
etc.Class
es la base de las metaclases que proporciona métodos comunes para todas las clases (aunque no es una metaclase en sí misma); "una clase" es algo así como Integer
, o Widget
, o Car
, etc.Metaclass
proporciona métodos comunes para todas las metaclases.Cuatro clases proporcionan las facilidades para describir nuevas clases. Su jerarquía de herencia (de Objeto) y las principales facilidades que brindan son:
Ruby purifica el concepto de metaclases de Smalltalk-80 al introducir clases propias, eliminar la Metaclass
clase y (des)redefinir el mapa de clases. El cambio se puede esquematizar de la siguiente manera: [6]
Tenga en cuenta en particular la correspondencia entre las metaclases implícitas de Smalltalk y las clases propias de clases de Ruby. El modelo de clase propia de Ruby hace que el concepto de metaclases implícitas sea completamente uniforme: cada objeto x tiene su propio metaobjeto, llamado clase propia de x , que es un metanivel superior a x . Las clases propias de "orden superior" generalmente existen puramente conceptualmente: no contienen ningún método ni almacenan ningún (otro) dato en la mayoría de los programas Ruby. [7]
Los siguientes diagramas muestran una estructura central de muestra de Smalltalk-80 y Ruby en comparación. [8]
En ambos idiomas, la estructura consta de una parte incorporada que contiene los objetos circulares (es decir, objetos que aparecen en un ciclo formado por una combinación de enlaces azules o verdes) y una parte de usuario que tiene cuatro objetos explícitos: clases A
y B
objetos terminales u
y v
. Los enlaces verdes muestran la relación de herencia hijo→padre (con la dirección ascendente implícita), los enlaces azules muestran la relación complementaria de creación de instancias miembro→contenedor (un enlace azul desde x apunta al contenedor menos real de x que es el punto de partida para la búsqueda de métodos cuando se invoca un método en x ). Los nodos grises muestran las clases propias (respectivamente, metaclases implícitas en el caso de Smalltalk-80).
El diagrama de la derecha también proporciona una imagen de la evaluación diferida de clases propias en Ruby. Al v
objeto se le puede evaluar (asignar) su clase propia como consecuencia de agregar métodos singleton a v
.
Según el método de introspección de Ruby denominado class
, la clase de cada clase (y de cada clase propia) es constantemente la Class
clase (indicada por c
en el diagrama). Class
y Struct
son las únicas clases que tienen clases como instancias. [9] [ disputado – discutir ] La subclasificación de Class
no está permitida. Siguiendo la definición estándar de metaclases podemos concluir que Class
y Struct
son las únicas metaclases en Ruby. Esto parece contradecir la correspondencia entre Ruby y Smalltalk, ya que en Smalltalk-80 cada clase tiene su propia metaclase. La discrepancia se basa en el desacuerdo entre el class
método de introspección en Ruby y Smalltalk. Mientras que el mapa x ↦ x.class
coincide en objetos terminales, se diferencia en la restricción a clases. Como ya se mencionó anteriormente, para una clase x
, la expresión Ruby x.class
se evalúa constantemente como Class
. En Smalltalk-80, si x
es una clase, entonces la expresión x class
corresponde a Ruby x.singleton_class
, que se evalúa como la clase propia de x
.
Las metaclases en Objective-C son casi las mismas que las de Smalltalk-80, lo que no es sorprendente ya que Objective-C toma prestado mucho de Smalltalk. Al igual que Smalltalk, en Objective-C, las variables y métodos de instancia están definidos por la clase de un objeto. Una clase es un objeto, por tanto es una instancia de una metaclase.
Al igual que Smalltalk, en Objective-C, los métodos de clase son simplemente métodos llamados en el objeto de clase, por lo tanto, los métodos de clase de una clase deben definirse como métodos de instancia en su metaclase. Debido a que diferentes clases pueden tener diferentes conjuntos de métodos de clase, cada clase debe tener su propia metaclase separada. Las clases y metaclases siempre se crean como un par: el tiempo de ejecución tiene funciones objc_allocateClassPair()
y objc_registerClassPair()
para crear y registrar pares clase-metaclase, respectivamente.
No hay nombres para las metaclases; sin embargo, se puede hacer referencia a un puntero a cualquier objeto de clase con el tipo genérico Class
(similar al tipo id
que se utiliza para un puntero a cualquier objeto).
Debido a que los métodos de clase se heredan a través de herencia, como Smalltalk, las metaclases deben seguir un esquema de herencia paralelo al de las clases (por ejemplo, si la clase padre de la clase A es la clase B, entonces la clase padre de la metaclase de A es la metaclase de B), excepto la de la clase raíz.
A diferencia de Smalltalk, la metaclase de la clase raíz hereda de la propia clase raíz (generalmente NSObject
usando el marco Cocoa ). Esto garantiza que todos los objetos de clase sean, en última instancia, instancias de la clase raíz, de modo que pueda utilizar los métodos de instancia de la clase raíz, normalmente métodos de utilidad útiles para objetos, en los propios objetos de clase.
Dado que los objetos de metaclase no se comportan de manera diferente (no se pueden agregar métodos de clase para una metaclase, por lo que todos los objetos de metaclase tienen los mismos métodos), todos son instancias de la misma clase: la metaclase de la clase raíz (a diferencia de Smalltalk). Por tanto, la metaclase de la clase raíz es una instancia de sí misma. La razón de esto es que todas las metaclases heredan de la clase raíz; por tanto, deben heredar los métodos de clase de la clase raíz. [10]
Los siguientes son algunos de los lenguajes de programación más destacados que admiten metaclases.
Algunos lenguajes menos extendidos que admiten metaclases incluyen OpenJava , OpenC++, OpenAda, CorbaScript , ObjVLisp, Object-Z , MODEL-K, XOTcl y MELDC. Varios de estos idiomas datan de principios de la década de 1990 y son de interés académico. [12]
Logtalk , una extensión de Prolog orientada a objetos , también admite metaclases.
El Marco de descripción de recursos (RDF) y el Lenguaje de modelado unificado (UML) admiten metaclases.