Buscar..


Introducción

Las metaclases le permiten modificar profundamente el comportamiento de las clases de Python (en términos de cómo se definen, se crean instancias, se accede a ellas, y más) al reemplazar la metaclase de type que las nuevas clases usan de forma predeterminada.

Observaciones

Al diseñar su arquitectura, tenga en cuenta que muchas cosas que se pueden lograr con las metaclases también se pueden lograr utilizando una semántica más simple:

  • La herencia tradicional es a menudo más que suficiente.
  • Los decoradores de clase pueden combinar la funcionalidad en una clase con un enfoque ad hoc.
  • Python 3.6 introduce __init_subclass__() que permite a una clase participar en la creación de su subclase.

Metaclases basicas

Cuando se llama a type con tres argumentos, se comporta como la clase (meta) que es y crea una nueva instancia, es decir. produce una nueva clase / tipo.

Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__              # <type 'type'> 
Dummy().__class__.__class__  # <type 'type'> 

Es posible subclase type para crear una metaclase personalizado.

class mytype(type):
    def __init__(cls, name, bases, dict):
        # call the base initializer
        type.__init__(cls, name, bases, dict)

        # perform custom initialization...
        cls.__custom_attribute__ = 2

Ahora, tenemos una nueva metaclase mytype personalizada que se puede utilizar para crear clases de la misma manera que el type .

MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__              # <class '__main__.mytype'>
MyDummy().__class__.__class__  # <class '__main__.mytype'>
MyDummy.__custom_attribute__   # 2

Cuando creamos una nueva clase utilizando la palabra clave de class la metaclase se elige de forma predeterminada en función de las clases básicas.

>>> class Foo(object):
...     pass

>>> type(Foo)
type

En el ejemplo anterior, la única clase de base es el object por lo que nuestra metaclase será el tipo de object , que es el type . Es posible anular el valor predeterminado, sin embargo, depende de si usamos Python 2 o Python 3:

Python 2.x 2.7

Un atributo de nivel de clase especial __metaclass__ se puede usar para especificar la metaclase.

class MyDummy(object):
    __metaclass__ = mytype
type(MyDummy)  # <class '__main__.mytype'>
Python 3.x 3.0

Un argumento especial de palabra clave de metaclass especifica la metaclase.

class MyDummy(metaclass=mytype):
    pass
type(MyDummy)  # <class '__main__.mytype'>

Cualquier argumento de palabra clave (excepto metaclass ) en la declaración de clase se pasará a la metaclase. Así, la class MyDummy(metaclass=mytype, x=2) pasará x=2 como argumento de palabra clave al constructor mytype .

Lea esta descripción detallada de las meta-clases de python para más detalles.

Singletons utilizando metaclases

Un singleton es un patrón que restringe la creación de instancias de una clase a una instancia / objeto. Para más información sobre los patrones de diseño singleton de python, consulte aquí .

class SingletonType(type):
    def __call__(cls, *args, **kwargs):
        try:
            return cls.__instance
        except AttributeError:
            cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
            return cls.__instance
Python 2.x 2.7
class MySingleton(object):
    __metaclass__ = SingletonType
Python 3.x 3.0
class MySingleton(metaclass=SingletonType):
    pass
MySingleton() is MySingleton()  # True, only one instantiation occurs

Usando una metaclase

Sintaxis de metaclase

Python 2.x 2.7
class MyClass(object):
    __metaclass__ = SomeMetaclass
Python 3.x 3.0
class MyClass(metaclass=SomeMetaclass):
    pass

Compatibilidad de Python 2 y 3 con six

import six

class MyClass(six.with_metaclass(SomeMetaclass)):
    pass

Funcionalidad personalizada con metaclases.

La funcionalidad de las metaclases se puede cambiar de modo que cada vez que se construye una clase, se imprime una cadena en la salida estándar o se lanza una excepción. Esta metaclase imprimirá el nombre de la clase que se está construyendo.

class VerboseMetaclass(type):

    def __new__(cls, class_name, class_parents, class_dict):
        print("Creating class ", class_name)
        new_class = super().__new__(cls, class_name, class_parents, class_dict)
        return new_class

Puedes usar la metaclase así:

class Spam(metaclass=VerboseMetaclass):
    def eggs(self):
        print("[insert example string here]")
s = Spam()
s.eggs()

La salida estándar será:

Creating class Spam
[insert example string here]

Introducción a las metaclases

¿Qué es una metaclase?

En Python, todo es un objeto: enteros, cadenas, listas, incluso las funciones y las clases en sí son objetos. Y cada objeto es una instancia de una clase.

Para verificar la clase de un objeto x, se puede llamar al type(x) , entonces:

>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>

>>> class C(object):
...     pass
...
>>> type(C)
<type 'type'>    

La mayoría de las clases en python son instancias de type . type sí mismo es también una clase. Tales clases cuyas instancias son también clases se llaman metaclases.

La metaclase mas simple

OK, entonces ya hay una metaclase en Python: type . ¿Podemos crear otro?

class SimplestMetaclass(type):
    pass

class MyClass(object):
    __metaclass__ = SimplestMetaclass

Eso no agrega ninguna funcionalidad, pero es una nueva metaclase, vea que MyClass ahora es una instancia de SimplestMetaclass:

>>> type(MyClass)
<class '__main__.SimplestMetaclass'>

Una metaclase que hace algo

Una metaclase el que hace algo por lo general prevalece sobre type 's __new__ , modificar algunas propiedades de la clase que se creen, antes de llamar al original __new__ que crea la clase:

class AnotherMetaclass(type):
    def __new__(cls, name, parents, dct):
        # cls is this class
        # name is the name of the class to be created
        # parents is the list of the class's parent classes
        # dct is the list of class's attributes (methods, static variables)

        # here all of the attributes can be modified before creating the class, e.g.

        dct['x'] = 8  # now the class will have a static variable x = 8

        # return value is the new class. super will take care of that
        return super(AnotherMetaclass, cls).__new__(cls, name, parents, dct)

La metaclase por defecto.

Es posible que hayas oído que todo en Python es un objeto. Es cierto, y todos los objetos tienen una clase:

>>> type(1)
int

El literal 1 es una instancia de int . Vamos a declarar una clase:

>>> class Foo(object):
...    pass
...

Ahora vamos a instanciarlo:

>>> bar = Foo()

¿Cuál es la clase de bar ?

>>> type(bar)
Foo

Agradable, el bar es una instancia de Foo . Pero, ¿cuál es la clase de Foo sí?

>>> type(Foo)
type

Ok, Foo sí es una instancia de type . ¿Qué tal el type sí mismo?

>>> type(type)
type

Entonces, ¿qué es una metaclase? Por ahora, simulemos que es solo un nombre elegante para la clase de una clase. Para llevar:

  • Todo es un objeto en Python, así que todo tiene una clase.
  • La clase de una clase se llama metaclase.
  • La metaclase predeterminada es de type y, con mucho, es la metaclase más común.

Pero ¿por qué debería saber acerca de las metaclases? Bueno, Python en sí mismo es bastante "hackeable", y el concepto de metaclase es importante si estás haciendo cosas avanzadas como meta-programación o si quieres controlar cómo se inicializan tus clases.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow