15 Pregunta: ¿Qué son las metaclases en Python?

pregunta creada en Thu, Jan 11, 2018 12:00 AM

¿Qué son las metaclases y para qué las utilizamos?

    
5184
15 Respuestas                              15                         

Una metaclase es la clase de una clase. Una clase define cómo se comporta una instancia de la clase (es decir, un objeto), mientras que una metaclase define cómo se comporta una clase. Una clase es una instancia de una metaclase.

Mientras estés en Python, puedes usar callables arbitrarios para metaclases (como Jerub muestra), el mejor enfoque es convertirlo en una clase en sí. type es la metaclase habitual en Python. type es en sí mismo una clase, y es su propio tipo. No podrás recrear algo como type puramente en Python, pero Python hace un poco de trampa. Para crear su propia metaclase en Python, realmente solo desea subclase type.

Una metaclase se usa más comúnmente como una clase de fábrica. Cuando creas un objeto llamando a la clase, Python crea una nueva clase (cuando ejecuta la declaración de 'clase') llamando a la metaclase. Combinadas con los métodos normales __init__ y __new__, las metaclases, por lo tanto, le permiten hacer "cosas adicionales" al crear una clase, como registrar la nueva clase con algún registro o reemplazar la clase con algo completamente distinto.

Cuando se ejecuta la sentencia class, Python ejecuta primero el cuerpo de la sentencia class como un bloque de código normal. El espacio de nombres resultante (un dict) contiene los atributos de la clase a ser. La metaclase se determina mirando las clases básicas de la clase a ser (las metaclases se heredan), el atributo __metaclass__ de la clase a ser (si existe) o la variable global __metaclass__. Luego se llama a la metaclase con el nombre, las bases y los atributos de la clase para instanciarla.

Sin embargo, las metaclases realmente definen el tipo de una clase, no solo una fábrica para él, por lo que puedes hacer mucho más con ellas. Por ejemplo, puede definir métodos normales en la metaclase. Estos métodos de metaclase son como métodos de clase, ya que pueden llamarse en la clase sin una instancia, pero tampoco son métodos de clase porque no pueden llamarse en una instancia de la clase. type.__subclasses__() es un ejemplo de un método en la metaclase type. También puede definir los métodos 'mágicos' normales, como __add__, __iter__ y __getattr__, para implementar o cambiar el comportamiento de la clase.

Aquí hay un ejemplo agregado de los bits y piezas:

 
def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
    
2512
2019-03-04 21: 34: 19Z
  1. class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
    2017-08-03 14: 34: 29Z
  2. ppperry obviamente quiso decir que no se puede recrear el tipo sin usar el tipo en sí mismo como una metaclase. Lo que es justo decirlo.
    2018-09-18 23: 24: 10Z
  3. ¿No debería invocarse a unregister () mediante una instancia de clase de Ejemplo?
    2018-11-29 00: 59: 15Z
  4. Tenga en cuenta que __metaclass__ no es compatible con Python 3. En Python 3 use class MyObject(metaclass=MyType), consulte python.org/dev/peps/pep-3115 y la respuesta a continuación.
    2019-05-01 08: 36: 00Z

Clases como objetos

Antes de entender las metaclases, necesitas dominar las clases en Python. Y Python tiene una idea muy peculiar de qué clases son, tomadas del lenguaje Smalltalk.

En la mayoría de los idiomas, las clases son solo piezas de código que describen cómo producir un objeto. Eso también es cierto en Python:

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

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Pero las clases son más que eso en Python. Las clases también son objetos.

Sí, objetos.

Tan pronto como use la palabra clave class, Pythen lo ejecuta y crea un objeto. La instruccion

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

crea en la memoria un objeto con el nombre "ObjectCreator".

Este objeto (la clase) es capaz de crear objetos (las instancias), y es por eso que es una clase .

Pero aún así, es un objeto, y por lo tanto:

  • puedes asignarlo a una variable
  • puedes copiarlo
  • puedes agregarle atributos
  • puedes pasarlo como un parámetro de función

por ejemplo:

 
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

Creando clases dinámicamente

Dado que las clases son objetos, puedes crearlos sobre la marcha, como cualquier objeto.

Primero, puedes crear una clase en una función usando class:

 
>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

Pero no es tan dinámico, ya que todavía tienes que escribir toda la clase.

Dado que las clases son objetos, deben generarse por algo.

Cuando usa la palabra clave class, Python crea este objeto automáticamente. Pero como Con la mayoría de las cosas en Python, te da una forma de hacerlo manualmente.

¿Recuerdas la función type? La buena función antigua que te permite saber qué. escribe un objeto es:

 
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

Bueno, type tiene una capacidad completamente diferente, también puede Crea clases sobre la marcha. type puede tomar la descripción de una clase como parámetros, y devolver una clase.

(Lo sé, es una tontería que la misma función pueda tener dos usos completamente diferentes de acuerdo con los parámetros que le pases. Es un problema debido al revés compatibilidad en Python)

type funciona de esta manera:

 
type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

por ejemplo:

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

se puede crear manualmente de esta manera:

 
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

Notarás que usamos "MyShinyClass" como el nombre de la clase y como la variable para mantener la referencia de clase. Pueden ser diferentes, pero no hay razón para complicar las cosas.

type acepta un diccionario para definir los atributos de la clase. Entonces:

 
>>> class Foo(object):
...       bar = True

Se puede traducir a:

 
>>> Foo = type('Foo', (), {'bar':True})

Y usado como una clase normal:

 
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

Y, por supuesto, puedes heredarlo, así que:

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

sería:

 
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

Eventualmente querrás agregar métodos a tu clase. Solo define una función con la firma adecuada y asignarlo como un atributo.

 
>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

Y puedes agregar incluso más métodos después de crear dinámicamente la clase, al igual que agregar métodos a un objeto de clase creado normalmente.

 
>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

Usted ve a dónde vamos: en Python, las clases son objetos, y puede crear una clase sobre la marcha, dinámicamente.

Esto es lo que hace Python cuando usa la palabra clave class, y lo hace usando una metaclase.

¿Qué son las metaclases (finalmente)

Las metaclases son las "cosas" que crean clases.

Usted define las clases para crear objetos, ¿verdad?

Pero aprendimos que las clases de Python son objetos.

Bueno, las metaclases son las que crean estos objetos. Son las clases de las clases. Puedes visualizarlos de esta manera:

 
MyClass = MetaClass()
my_object = MyClass()

Has visto que type te permite hacer algo como esto:

 
MyClass = type('MyClass', (), {})

Es porque la función type es de hecho una metaclase. type es el Metaclass Python utiliza para crear todas las clases detrás de escena.

Ahora se pregunta por qué diablos está escrito en minúsculas, y no Type?

Bueno, creo que es una cuestión de coherencia con str, la clase que crea cadenas de objetos, y int la clase que crea objetos enteros. type es solo la clase que crea objetos de clase.

Lo ves al verificar el atributo __class__.

Todo, y lo digo todo, es un objeto en Python. Eso incluye ints, Cuerdas, funciones y clases. Todos ellos son objetos. Y todos ellos tienen creado desde una clase:

 
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Ahora, ¿cuál es el __class__ de cualquier __class__?

 
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Entonces, una metaclase es solo lo que crea objetos de clase.

Puedes llamarlo 'fábrica de clases' si lo deseas.

type es la metaclase incorporada que Python usa, pero por supuesto, puedes crear tu metaclase propia.

El __metaclass__ atributo

En Python 2, puede agregar un atributo __metaclass__ cuando escribe una clase (vea la siguiente sección para la sintaxis de Python 3):

 
class Foo(object):
    __metaclass__ = something...
    [...]

Si lo haces, Python usará la metaclase para crear la clase Foo.

Cuidado, es complicado.

Primero escribe class Foo(object), pero el objeto de clase Foo no se crea en la memoria todavía.

Python buscará __metaclass__ en la definición de clase. Si lo encuentra, lo usará para crear la clase de objeto Foo. Si no lo hace, utilizará  type para crear la clase.

Lee eso varias veces.

Cuando lo hagas:

 
class Foo(Bar):
    pass

Python hace lo siguiente:

hay un__metaclass__ atributo en Foo?

Si es así, crea en la memoria un objeto de clase (dije un objeto de clase, quédate conmigo aquí), con el nombre Foo usando lo que está en __metaclass__.

Si Python no puede encontrar __metaclass__, buscará un __metaclass__ a nivel de MÓDULO e intentará hacer lo mismo (pero solo para las clases que no heredan nada, básicamente clases de estilo antiguo).

Luego, si no encuentra ningún __metaclass__, usará la metaclase del Bar (el primer padre) (que podría ser el type predeterminado) para crear el objeto de clase.

Tenga cuidado aquí de que el atributo __metaclass__ no se heredará, la metaclase del padre (Bar.__class__) será heredado. Si Bar usó un atributo __metaclass__ que creó Bar con type() (y no type.__new__()), las subclases no heredarán ese comportamiento.

Ahora la gran pregunta es, ¿qué puedes poner en __metaclass__?

La respuesta es: algo que puede crear una clase.

¿Y qué puede crear una clase? type, o cualquier cosa que subclasifique o use.

Metaclases en Python 3

La sintaxis para establecer la metaclase se ha cambiado en Python 3:

 
class Foo(object, metaclass=something):
    ...

es decir, el atributo __metaclass__ ya no se usa, a favor de un argumento de palabra clave en la lista de clases base.

Sin embargo, el comportamiento de las metaclases sigue siendo en gran medida el mismo .

Una cosa agregada a las metaclases en Python 3 es que también puedes pasar atributos como argumentos de palabras clave a una metaclase, de esta manera:

 
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

Lee la siguiente sección para saber cómo Python maneja esto.

Metaclases personalizadas

El propósito principal de una metaclase es cambiar la clase automáticamente, cuando se crea.

Por lo general, hace esto para las API, donde desea crear clases que coincidan con las contexto actual.

Imagina un ejemplo estúpido, donde decides que todas las clases en tu módulo Deben tener sus atributos escritos en mayúsculas. Hay varias maneras de haga esto, pero una forma es establecer __metaclass__ en el nivel del módulo.

De esta manera, todas las clases de este módulo se crearán usando esta metaclase, y solo tenemos que decirle a la metaclase que convierta todos los atributos en mayúsculas.

Afortunadamente, el __metaclass__ puede ser realmente invocable, no necesita ser un clase formal (lo sé, algo con 'clase' en su nombre no necesita ser una clase, imagínate ... pero es útil).

Comenzaremos con un ejemplo simple, utilizando una función.

 
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

Ahora, hagamos exactamente lo mismo, pero usando una clase real para una metaclase:

 
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

Pero esto no es realmente OOP. Llamamos directamente al type y no anulamos o llame al padre __new__. Hagámoslo:

 
class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

Puede que hayas notado el argumento extra upperattr_metaclass. Ahi esta no tiene nada de especial: __new__ siempre recibe la clase en la que está definido, como primer parámetro. Al igual que tiene self para los métodos ordinarios que reciben la instancia como primer parámetro, o la clase definitoria para los métodos de clase.

Por supuesto, los nombres que utilicé aquí son largos para mayor claridad, pero me gusta Para self, todos los argumentos tienen nombres convencionales. Así que una producción real. La metaclase se vería así:

 
class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

Podemos hacerlo aún más limpio utilizando super, lo que facilitará la herencia (porque sí, puede tener metaclases, heredar de metaclases, heredar de tipo):

 
class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

Ah, y en Python 3 si haces esta llamada con argumentos de palabras clave, como este:

 
class Foo(object, metaclass=Thing, kwarg1=value1):
    ...

Se traduce a esto en la metaclase para usarlo:

 
class Thing(type):
    def __new__(cls, clsname, bases, dct, kwargs1=default):
        ...

Eso es todo. Realmente no hay nada más acerca de las metaclases.

La razón detrás de la complejidad del código usando metaclases no es porque de metaclases, es porque usualmente usas metaclases para hacer cosas retorcidas confiar en la introspección, la manipulación de la herencia, vars como __dict__, etc.

De hecho, las metaclases son especialmente útiles para hacer magia negra, y por lo tanto cosas complicadas Pero por sí solos, son simples:

  • interceptar una creación de clase
  • modificar la clase
  • devolver la clase modificada

¿Por qué usarías clases de metaclases en lugar de funciones?

Ya que __metaclass__ puede aceptar cualquier llamada, ¿por qué usaría una clase? ya que obviamente es mas complicado?

Hay varias razones para hacerlo:

  • La intención es clara. Cuando lees UpperAttrMetaclass(type), sabes lo que va a seguir
  • Puedes usar OOP. La metaclase puede heredar de la metaclase, anular los métodos primarios. Las metaclases pueden incluso utilizar metaclases.
  • Las subclases de una clase serán instancias de su metaclase si ha especificado una clase de metaclase, pero no con una función de metaclase.
  • Puedes estructurar mejor tu código. Nunca usas metaclases para algo como Trivial como el ejemplo anterior. Por lo general es para algo complicado. Teniendo el Capacidad de hacer varios métodos yg.Roup ellos en una clase es muy útil para que el código sea más fácil de leer.
  • Puede enganchar en __new__, __init__ y __call__. El cual permitirá que hagas cosas diferentes Incluso si normalmente puedes hacerlo todo en __new__, algunas personas se sienten más cómodas usando __init__.
  • Estas se llaman metaclases, ¡maldita sea! ¡Debe significar algo!

¿Por qué usarías metaclases?

Ahora la gran pregunta. ¿Por qué usaría alguna característica oscura propensa a errores?

Bueno, normalmente no lo haces:

  

Las metaclases son magia más profunda que   El 99% de los usuarios nunca deben preocuparse.   Si te preguntas si las necesitas,   usted no (la gente que realmente   Necesito que sepan con certeza que   Los necesitan, y no necesitan un   explicación sobre por qué).

Python Guru Tim Peters

El caso de uso principal de una metaclase es crear una API. Un ejemplo típico de esto es el ORM de Django.

Te permite definir algo como esto:

 
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

Pero si haces esto:

 
guy = Person(name='bob', age='35')
print(guy.age)

No devolverá un objeto IntegerField. Devolverá un int, e incluso puede tomarlo directamente de la base de datos.

Esto es posible porque models.Model define __metaclass__ y utiliza algo de magia que convertirá el Person que acaba de definir con declaraciones simples en un enlace complejo a un campo de base de datos.

Django hace que algo complejo parezca simple al exponer una API simple y usando metaclases, recreando código de esta API para hacer el trabajo real detrás de las escenas.

La última palabra

Primero, sabes que las clases son objetos que pueden crear instancias.

Bueno, de hecho, las clases son en sí mismas instancias. De metaclases.

 
>>> class Foo(object): pass
>>> id(Foo)
142630324

Todo es un objeto en Python, y todos son instancias de clases o instancias de metaclases.

Excepto type.

type es en realidad su propia metaclase. Esto no es algo que puedas Reproducir en Python puro, y se hace haciendo trampa un poco en la implementación nivel.

En segundo lugar, las metaclases son complicadas. Es posible que no desee utilizarlos para Alteraciones de clase muy simples. Puedes cambiar las clases usando dos técnicas diferentes:

El 99% del tiempo que necesita una modificación de clase, es mejor que use estos.

Pero el 98% de las veces, no necesita ninguna modificación de clase.

    
6304
2019-04-16 17: 55: 24Z
  1. Parece que en Django models.Model no usa __metaclass__ sino class Model(metaclass=ModelBase): para hacer referencia a una clase ModelBase que luego hace la magia de metaclase antes mencionada. ¡Buena publicación! Aquí está la fuente de Django: github.com/django /django /blob /master /django /db /models /…
    2017-04-12 13: 18: 59Z
  2. ¿qué sucede si se indica metaclase diferente tanto en la clase base como en la clase derivada?
    2017-04-20 03: 24: 08Z
  3. < < Tenga cuidado aquí de que el atributo __metaclass__ no se heredará, la metaclase del padre (Bar.__class__). Si Bar usó un atributo __metaclass__ que creó Bar con type() (y no type.__new__()), las subclases no heredarán ese comportamiento. &Gt; > - ¿Podría usted /alguien explicar un poco más profundo este pasaje?
    2017-04-25 21: 32: 44Z
  4. @ MaxGoodridge Esa es la sintaxis de Python 3 para las metaclases. Consulte Modelo de datos de Python 3.6 VS Modelo de datos de Python 2.7
    2017-06-13 13: 22: 27Z
  5. Es una respuesta de la wiki de la comunidad (por lo tanto, aquellos que comentaron con correcciones /mejoras podrían considerar editar sus comentarios en la respuesta, si están seguros de que son correctos).
    2017-11-08 08: 59: 31Z

    Nota, esta respuesta es para Python 2.x como se escribió en 2008, las metaclases son ligeramente diferentes en 3.x.

    Las metaclases son la salsa secreta que hace que la clase funcione. La metaclase predeterminada para un nuevo objeto de estilo se llama "tipo".

     
    class type(object)
      |  type(object) -> the object's type
      |  type(name, bases, dict) -> a new type
    

    Las metaclases toman 3 args. ' nombre ', ' bases ' y ' dict '

    Aquí es donde comienza el secreto. Busque de dónde vienen el nombre, las bases y el dictado en esta definición de clase de ejemplo.

     
    class ThisIsTheName(Bases, Are, Here):
        All_the_code_here
        def doesIs(create, a):
            dict
    

    Permite definir una metaclase que demostrará cómo ' class: ' lo llama.

     
    def test_metaclass(name, bases, dict):
        print 'The Class Name is', name
        print 'The Class Bases are', bases
        print 'The dict has', len(dict), 'elems, the keys are', dict.keys()
    
        return "yellow"
    
    class TestName(object, None, int, 1):
        __metaclass__ = test_metaclass
        foo = 1
        def baz(self, arr):
            pass
    
    print 'TestName = ', repr(TestName)
    
    # output => 
    The Class Name is TestName
    The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
    The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
    TestName =  'yellow'
    

    Y ahora, un ejemplo que realmente significa algo, esto hará que las variables en la lista de "atributos" se establezcan en la clase, y se establezca en Ninguno.

     
    def init_attributes(name, bases, dict):
        if 'attributes' in dict:
            for attr in dict['attributes']:
                dict[attr] = None
    
        return type(name, bases, dict)
    
    class Initialised(object):
        __metaclass__ = init_attributes
        attributes = ['foo', 'bar', 'baz']
    
    print 'foo =>', Initialised.foo
    # output=>
    foo => None
    

    Tenga en cuenta que el comportamiento mágico que "Initalizado" gana al tener la metaclase init_attributes no se pasa a una subclase de Initalizado.

    Este es un ejemplo aún más concreto, que muestra cómo puede subclasificar 'tipo' para hacer una metaclase que realice una acción cuando se crea la clase. Esto es bastante complicado:

     
    class MetaSingleton(type):
        instance = None
        def __call__(cls, *args, **kw):
            if cls.instance is None:
                cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
            return cls.instance
    
     class Foo(object):
         __metaclass__ = MetaSingleton
    
     a = Foo()
     b = Foo()
     assert a is b
    
        
    359
    2019-05-13 07: 55: 44Z

    Un uso para las metaclases es agregar nuevas propiedades y métodos a una instancia automáticamente.

    Por ejemplo, si observa modelos de Django , sus La definición parece un poco confusa. Parece como si solo estuvieras definiendo propiedades de clase:

     
    class Person(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)
    

    Sin embargo, en tiempo de ejecución, los objetos Person se llenan con todo tipo de métodos útiles. Consulte la fuente para ver algunos metaclases sorprendentes.     

    146
    2008-09-19 06: 45: 40Z
    1. ¿El uso de metaclases no agrega nuevas propiedades y métodos a una clase y no es una instancia? Por lo que yo entendí, la clase meta altera la clase en sí misma y, como resultado, las clases pueden construirse de manera diferente por la clase alterada. Podría ser un poco engañoso para las personas que intentan obtener la naturaleza de una clase meta. Tener métodos útiles en casos puede lograrse por la normalidad. Sin embargo, la referencia al código de Django como ejemplo es buena.
      2017-01-27 23: 24: 42Z

    Otros han explicado cómo funcionan las metaclases y cómo encajan en el sistema de tipo Python. Aquí hay un ejemplo de para qué se pueden usar. En un marco de prueba que escribí, quería hacer un seguimiento del orden en el que se definían las clases, para poder luego instanciarlas en este orden. Me resultó más fácil hacer esto usando una metaclase.

     
    class MyMeta(type):
    
        counter = 0
    
        def __init__(cls, name, bases, dic):
            type.__init__(cls, name, bases, dic)
            cls._order = MyMeta.counter
            MyMeta.counter += 1
    
    class MyType(object):              # Python 2
        __metaclass__ = MyMeta
    
    class MyType(metaclass=MyMeta):    # Python 3
        pass
    

    Cualquier cosa que sea una subclase de MyType obtiene un atributo de clase _order que registra el orden en que se definieron las clases.

        
    146
    2016-11-28 18: 04: 41Z
    1. Gracias por el ejemplo. ¿Por qué encontraste esto más fácil que heredarlo de MyBase, cuyo __init__(self) dice type(self)._order = MyBase.counter; MyBase.counter += 1?
      2019-04-16 17: 59: 34Z
    2. Quería que las clases en sí mismas, no sus instancias, fueran numeradas.
      2019-04-16 18: 09: 39Z
    3. Derecho, duh. Gracias. Mi código restablecería el atributo de MyType en cada instanciación, y nunca establecería el atributo si nunca se creara una instancia de MyType. Ups. (Y una propiedad de clase también podría funcionar, pero a diferencia de la metaclase, no ofrece un lugar obvio para almacenar el contador).
      2019-04-17 21: 58: 00Z

    Creo que la ÚNICA introducción a la programación de metaclase está bien escrita y ofrece una muy buena introducción al tema a pesar de que ya tiene varios años.

    http://www.onlamp.com/pub /a /python /2003/04/17 /metaclasses.html (archivado en https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a /python/2003/04/17/metaclasses.html )

    En resumen: una clase es un plan para la creación de una instancia, una metaclase es un plan para la creación de una clase. Se puede ver fácilmente que en Python las clases también deben ser objetos de primera clase para habilitar este comportamiento.

    Nunca escribí uno, pero creo que uno de los mejores usos de las metaclases se puede ver en el marco de Django . Las clases modelo utilizan un enfoque de metaclase para habilitar un estilo declarativo de escritura de nuevos modelos o clases de formulario. Mientras la metaclase crea la clase, todos los miembros tienen la posibilidad de personalizar la clase en sí.

    Lo que queda por decir es: si no sabes qué son las metaclases, la probabilidad de que no las necesites es del 99%.

        
    106
    2018-08-13 04: 53: 15Z
      

    ¿Qué son las metaclases? ¿Para qué los usas?

    TLDR: una metaclase crea una instancia y define el comportamiento de una clase, al igual que una clase crea una instancia y define el comportamiento de una instancia

    Pseudocódigo:

     
    >>> Class(...)
    instance
    

    Lo anterior debe parecer familiar. Bueno, ¿de dónde viene Class? Es una instancia de una metaclase (también pseudocódigo):

     
    >>> Metaclass(...)
    Class
    

    En código real, podemos pasar la metaclase predeterminada, type, todo lo que necesitamos para crear una clase y obtenemos una clase:

     
    >>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
    <class '__main__.Foo'>
    

    Poniéndolo de manera diferente

    • Una clase es para una instancia como una metaclase es para una clase.

      Cuando creamos una instancia de un objeto, obtenemos una instancia:

       
      >>> object()                          # instantiation of class
      <object object at 0x7f9069b4e0b0>     # instance
      

      Del mismo modo, cuando definimos una clase explícitamente con la metaclase predeterminada, type, la instanciamos:

       
      >>> type('Object', (object,), {})     # instantiation of metaclass
      <class '__main__.Object'>             # instance
      
    • Dicho de otra manera, una clase es una instancia de una metaclase:

       
      >>> isinstance(object, type)
      True
      
    • Dicho de otra forma, una metaclase es una clase de clase.

       
      >>> type(object) == type
      True
      >>> object.__class__
      <class 'type'>
      

    Cuando escribes una definición de clase y Python la ejecuta, usa una metaclase para crear una instancia del objeto de clase (que, a su vez, se usará para crear instancias de esa clase).

    Así como podemos usar definiciones de clase para cambiar el comportamiento de las instancias de objetos personalizados, podemos usar una definición de clase de metaclase para cambiar la forma en que se comporta un objeto de clase.

    ¿Para qué se pueden usar? De los docs :

      

    Los usos potenciales de las metaclases son ilimitados. Algunas ideas que se han explorado incluyen el registro, la verificación de la interfaz, la delegación automática, la creación automática de propiedades, los proxies, los marcos y el bloqueo /sincronización automática de recursos.

    Sin embargo, generalmente se recomienda a los usuarios evitar el uso de metaclases a menos que sea absolutamente necesario.

    Utiliza una metaclase cada vez que crea una clase:

    Cuando escribes una definición de clase, por ejemplo, como esta,

     
    class Foo(object): 
        'demo'
    

    Se crea una instancia de un objeto de clase.

     
    >>> Foo
    <class '__main__.Foo'>
    >>> isinstance(Foo, type), isinstance(Foo, object)
    (True, True)
    

    Es lo mismo que llamar funcionalmente al type con los argumentos apropiados y asignar el resultado a una variable de ese nombre:

     
    name = 'Foo'
    bases = (object,)
    namespace = {'__doc__': 'demo'}
    Foo = type(name, bases, namespace)
    

    Tenga en cuenta que algunas cosas se agregan automáticamente al __dict__, es decir, el espacio de nombres:

     
    >>> Foo.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
    '__module__': '__main__', '__weakref__': <attribute '__weakref__' 
    of 'Foo' objects>, '__doc__': 'demo'})
    

    La metaclase del objeto que creamos, en ambos casos, es type.

    (Una nota al margen sobre el contenido de la clase __dict__: __module__ está ahí porque las clases deben saber dónde están definidas, y __dict__ y __weakref__ están ahí porque no definimos __slots__, si define __slots__ guardaremos un poco de espacio en las instancias, ya que podemos disallow __dict__ y __weakref__ excluyéndolos. Por ejemplo:

     
    >>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
    >>> Baz.__dict__
    mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})
    

    ... pero estoy divagando.)

    Podemos extender type como cualquier otra definición de clase:

    Aquí está el __repr__ predeterminado de clases:

     
    >>> Foo
    <class '__main__.Foo'>
    

    Una de las cosas más valiosas que podemos hacer de manera predeterminada al escribir un objeto de Python es proporcionarle un buen __repr__. Cuando llamamos al help(repr), aprendemos que hay una buena prueba para un __repr__ que también requiere una prueba de igualdad - obj == eval(repr(obj)). La siguiente implementación simple de __repr__ y __eq__ para las instancias de clase de nuestra clase de tipo nos proporciona una demostración que puede mejorar en el __repr__ predeterminado de clases:

     
    class Type(type):
        def __repr__(cls):
            """
            >>> Baz
            Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
            >>> eval(repr(Baz))
            Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
            """
            metaname = type(cls).__name__
            name = cls.__name__
            parents = ', '.join(b.__name__ for b in cls.__bases__)
            if parents:
                parents += ','
            namespace = ', '.join(': '.join(
              (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
                   for k, v in cls.__dict__.items())
            return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
        def __eq__(cls, other):
            """
            >>> Baz == eval(repr(Baz))
            True            
            """
            return (cls.__name__, cls.__bases__, cls.__dict__) == (
                    other.__name__, other.__bases__, other.__dict__)
    

    Así que ahora, cuando creamos un objeto con esta metaclase, el __repr__ con eco en la línea de comandos proporciona una visión mucho menos fea que la predeterminada:

     
    >>> class Bar(object): pass
    >>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
    >>> Baz
    Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
    

    Con un buen __repr__ definido para la instancia de clase, tenemos una mayor capacidad para depurar nuestro código. Sin embargo, es poco probable que se realicen más comprobaciones con el eval(repr(Class)) (ya que las funciones serían bastante imposibles de evaluar a partir de sus __repr__ predeterminados).

    Un uso esperado: __prepare__ un espacio de nombres

    Si, por ejemplo, queremos saber en qué orden se crean los métodos de una clase, podríamos proporcionar un dictado ordenado como el espacio de nombres de la clase. Haríamos esto con __prepare__, que devuelve el espacio de nombres dict para clase si se implementa en Python 3 :

     
    from collections import OrderedDict
    
    class OrderedType(Type):
        @classmethod
        def __prepare__(metacls, name, bases, **kwargs):
            return OrderedDict()
        def __new__(cls, name, bases, namespace, **kwargs):
            result = Type.__new__(cls, name, bases, dict(namespace))
            result.members = tuple(namespace)
            return result
    

    Y uso:

     
    class OrderedMethodsObject(object, metaclass=OrderedType):
        def method1(self): pass
        def method2(self): pass
        def method3(self): pass
        def method4(self): pass
    

    Y ahora tenemos un registro del orden en que se crearon estos métodos (y otros atributos de clase):

     
    >>> OrderedMethodsObject.members
    ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')
    

    Nota, este ejemplo se adaptó de la documentación - el nuevo enumeración en la biblioteca estándar hace esto.

    Entonces, lo que hicimos fue instanciar una metaclase creando una clase. También podemos tratar la metaclase como lo haríamos con cualquier otra clase. Tiene un orden de resolución de método:

     
    >>> inspect.getmro(OrderedType)
    (<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)
    

    Y tiene aproximadamente el repr correcto (que ya no podemos evaluar a menos que podamos encontrar una manera de representar nuestras funciones):

     
    >>> OrderedMethodsObject
    OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
    hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
    ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
    
        
    93
    2017-08-30 03: 19: 14Z

    Actualización de Python 3

    Hay (en este punto) dos métodos clave en una metaclase:

    •  __prepare__, y
    • __new__

    __prepare__ le permite proporcionar una asignación personalizada (como OrderedDict) para usarla como espacio de nombres mientras se crea la clase. Debe devolver una instancia del espacio de nombres que elija. Si no implementas __prepare__, se utiliza un dict normal.

    __new__ es responsable de la creación /modificación real de la clase final.

    A una metaclase básica, sin hacer nada adicional, le gustaría:

     
    class Meta(type):
    
        def __prepare__(metaclass, cls, bases):
            return dict()
    
        def __new__(metacls, cls, bases, clsdict):
            return super().__new__(metacls, cls, bases, clsdict)
    

    Un ejemplo simple:

    Diga que desea que se ejecute un código de validación simple en sus atributos, como que siempre debe ser un int o un str. Sin una metaclase, su clase se vería como:

     
    class Person:
        weight = ValidateType('weight', int)
        age = ValidateType('age', int)
        name = ValidateType('name', str)
    

    Como puede ver, debe repetir el nombre del atributo dos veces. Esto hace posible los errores tipográficos junto con errores irritantes.

    Una metaclase simple puede resolver ese problema:

     
    class Person(metaclass=Validator):
        weight = ValidateType(int)
        age = ValidateType(int)
        name = ValidateType(str)
    

    Este es el aspecto que tendría la metaclase (sin usar __prepare__, ya que no es necesario):

     
    class Validator(type):
        def __new__(metacls, cls, bases, clsdict):
            # search clsdict looking for ValidateType descriptors
            for name, attr in clsdict.items():
                if isinstance(attr, ValidateType):
                    attr.name = name
                    attr.attr = '_' + name
            # create final class and return it
            return super().__new__(metacls, cls, bases, clsdict)
    

    Una ejecución de muestra de:

     
    p = Person()
    p.weight = 9
    print(p.weight)
    p.weight = '9'
    

    produce:

     
    9
    Traceback (most recent call last):
      File "simple_meta.py", line 36, in <module>
        p.weight = '9'
      File "simple_meta.py", line 24, in __set__
        (self.name, self.type, value))
    TypeError: weight must be of type(s) <class 'int'> (got '9')
    

    Nota : este ejemplo es bastante simple, también se podría haber logrado con un decorador de clase, pero probablemente una metaclase real estaría haciendo mucho más.

    La clase 'ValidateType' para referencia:

     
    class ValidateType:
        def __init__(self, type):
            self.name = None  # will be set by metaclass
            self.attr = None  # will be set by metaclass
            self.type = type
        def __get__(self, inst, cls):
            if inst is None:
                return self
            else:
                return inst.__dict__[self.attr]
        def __set__(self, inst, value):
            if not isinstance(value, self.type):
                raise TypeError('%s must be of type(s) %s (got %r)' %
                        (self.name, self.type, value))
            else:
                inst.__dict__[self.attr] = value
    
        
    67
    2016-03-01 19: 48: 34Z

    Función de un método __call__() de metaclase al crear una instancia de clase

    Si has hecho programación en Python durante más de unos pocos meses, eventualmente te encontrarás con un código que se ve así:

     
    # define a class
    class SomeClass(object):
        # ...
        # some definition here ...
        # ...
    
    # create an instance of it
    instance = SomeClass()
    
    # then call the object as if it's a function
    result = instance('foo', 'bar')
    

    Esto último es posible cuando implementas el método mágico __call__() en la clase.

     
    class SomeClass(object):
        # ...
        # some definition here ...
        # ...
    
        def __call__(self, foo, bar):
            return bar + foo
    

    El método __call__() se invoca cuando una instancia de una clase se usa como una llamada. Pero como hemos visto en respuestas anteriores, una clase en sí misma es una instancia de una metaclase, por lo tanto, cuando usamos la clase como invocable (es decir, cuando creamos una instancia).de eso) en realidad estamos llamando a su metaclase '__call__() método. En este punto, la mayoría de los programadores de Python están un poco confundidos porque se les ha dicho que al crear una instancia como esta instance = SomeClass() está llamando a su método __init__(). Algunos de los que han cavado un poco más profundo saben que antes del __init__() hay __new__(). Bueno, hoy se está revelando otra capa de verdad, antes de __new__() está la metaclase '__call__().

    Estudiemos la cadena de llamadas al método desde la perspectiva específica de crear una instancia de una clase.

    Esta es una metaclase que registra exactamente el momento antes de crear una instancia y el momento en que está a punto de devolverla.

     
    class Meta_1(type):
        def __call__(cls):
            print "Meta_1.__call__() before creating an instance of ", cls
            instance = super(Meta_1, cls).__call__()
            print "Meta_1.__call__() about to return instance."
            return instance
    

    Esta es una clase que usa esa metaclase

     
    class Class_1(object):
    
        __metaclass__ = Meta_1
    
        def __new__(cls):
            print "Class_1.__new__() before creating an instance."
            instance = super(Class_1, cls).__new__(cls)
            print "Class_1.__new__() about to return instance."
            return instance
    
        def __init__(self):
            print "entering Class_1.__init__() for instance initialization."
            super(Class_1,self).__init__()
            print "exiting Class_1.__init__()."
    

    Y ahora vamos a crear una instancia de Class_1

     
    instance = Class_1()
    # Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
    # Class_1.__new__() before creating an instance.
    # Class_1.__new__() about to return instance.
    # entering Class_1.__init__() for instance initialization.
    # exiting Class_1.__init__().
    # Meta_1.__call__() about to return instance.
    

    Observe que el código anterior no hace nada más que registrar las tareas. Cada método delega el trabajo real a la implementación de sus padres, manteniendo así el comportamiento predeterminado. Dado que type es la clase primaria de Meta_1 (siendo type la metaclase principal predeterminada) y considerando la secuencia de ordenamiento de la salida anterior, ahora tenemos una idea de cuál sería la pseudo implementación de type.__call__():

     
    class type:
        def __call__(cls, *args, **kwarg):
    
            # ... maybe a few things done to cls here
    
            # then we call __new__() on the class to create an instance
            instance = cls.__new__(cls, *args, **kwargs)
    
            # ... maybe a few things done to the instance here
    
            # then we initialize the instance with its __init__() method
            instance.__init__(*args, **kwargs)
    
            # ... maybe a few more things done to instance here
    
            # then we return it
            return instance
    

    Podemos ver que el método __call__() de metaclase es el que se llama primero. Luego delega la creación de la instancia al método __new__() de la clase y la inicialización a la __init__() de la instancia. También es el que finalmente devuelve la instancia.

    De lo anterior se desprende que la metaclase '__call__() también tiene la oportunidad de decidir si se realizará o no una llamada al Class_1.__new__() o Class_1.__init__(). En el transcurso de su ejecución, podría devolver un objeto que no haya sido tocado por ninguno de estos métodos. Tomemos, por ejemplo, este enfoque del patrón singleton:

     
    class Meta_2(type):
        singletons = {}
    
        def __call__(cls, *args, **kwargs):
            if cls in Meta_2.singletons:
                # we return the only instance and skip a call to __new__()
                # and __init__()
                print ("{} singleton returning from Meta_2.__call__(), "
                       "skipping creation of new instance.".format(cls))
                return Meta_2.singletons[cls]
    
            # else if the singleton isn't present we proceed as usual
            print "Meta_2.__call__() before creating an instance."
            instance = super(Meta_2, cls).__call__(*args, **kwargs)
            Meta_2.singletons[cls] = instance
            print "Meta_2.__call__() returning new instance."
            return instance
    
    class Class_2(object):
    
        __metaclass__ = Meta_2
    
        def __new__(cls, *args, **kwargs):
            print "Class_2.__new__() before creating instance."
            instance = super(Class_2, cls).__new__(cls)
            print "Class_2.__new__() returning instance."
            return instance
    
        def __init__(self, *args, **kwargs):
            print "entering Class_2.__init__() for initialization."
            super(Class_2, self).__init__()
            print "exiting Class_2.__init__()."
    

    Observemos lo que sucede cuando intentamos crear repetidamente un objeto del tipo Class_2

     
    a = Class_2()
    # Meta_2.__call__() before creating an instance.
    # Class_2.__new__() before creating instance.
    # Class_2.__new__() returning instance.
    # entering Class_2.__init__() for initialization.
    # exiting Class_2.__init__().
    # Meta_2.__call__() returning new instance.
    
    b = Class_2()
    # <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
    
    c = Class_2()
    # <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
    
    a is b is c # True
    
        
    58
    2018-08-27 17: 21: 01Z

    Una metaclase es una clase que dice cómo (otra) clase se debe crear.

    Este es un caso donde vi metaclass como una solución a mi problema: Tenía un problema realmente complicado, que probablemente podría haber sido resuelto de manera diferente, pero elegí resolverlo usando una metaclase. Debido a la complejidad, es uno de los pocos módulos que he escrito donde los comentarios en el módulo superan la cantidad de código que se ha escrito. Aquí está ...

     
    #!/usr/bin/env python
    
    # Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.
    
    # This requires some explaining.  The point of this metaclass excercise is to
    # create a static abstract class that is in one way or another, dormant until
    # queried.  I experimented with creating a singlton on import, but that did
    # not quite behave how I wanted it to.  See now here, we are creating a class
    # called GsyncOptions, that on import, will do nothing except state that its
    # class creator is GsyncOptionsType.  This means, docopt doesn't parse any
    # of the help document, nor does it start processing command line options.
    # So importing this module becomes really efficient.  The complicated bit
    # comes from requiring the GsyncOptions class to be static.  By that, I mean
    # any property on it, may or may not exist, since they are not statically
    # defined; so I can't simply just define the class with a whole bunch of
    # properties that are @property @staticmethods.
    #
    # So here's how it works:
    #
    # Executing 'from libgsync.options import GsyncOptions' does nothing more
    # than load up this module, define the Type and the Class and import them
    # into the callers namespace.  Simple.
    #
    # Invoking 'GsyncOptions.debug' for the first time, or any other property
    # causes the __metaclass__ __getattr__ method to be called, since the class
    # is not instantiated as a class instance yet.  The __getattr__ method on
    # the type then initialises the class (GsyncOptions) via the __initialiseClass
    # method.  This is the first and only time the class will actually have its
    # dictionary statically populated.  The docopt module is invoked to parse the
    # usage document and generate command line options from it.  These are then
    # paired with their defaults and what's in sys.argv.  After all that, we
    # setup some dynamic properties that could not be defined by their name in
    # the usage, before everything is then transplanted onto the actual class
    # object (or static class GsyncOptions).
    #
    # Another piece of magic, is to allow command line options to be set in
    # in their native form and be translated into argparse style properties.
    #
    # Finally, the GsyncListOptions class is actually where the options are
    # stored.  This only acts as a mechanism for storing options as lists, to
    # allow aggregation of duplicate options or options that can be specified
    # multiple times.  The __getattr__ call hides this by default, returning the
    # last item in a property's list.  However, if the entire list is required,
    # calling the 'list()' method on the GsyncOptions class, returns a reference
    # to the GsyncListOptions class, which contains all of the same properties
    # but as lists and without the duplication of having them as both lists and
    # static singlton values.
    #
    # So this actually means that GsyncOptions is actually a static proxy class...
    #
    # ...And all this is neatly hidden within a closure for safe keeping.
    def GetGsyncOptionsType():
        class GsyncListOptions(object):
            __initialised = False
    
        class GsyncOptionsType(type):
            def __initialiseClass(cls):
                if GsyncListOptions._GsyncListOptions__initialised: return
    
                from docopt import docopt
                from libgsync.options import doc
                from libgsync import __version__
    
                options = docopt(
                    doc.__doc__ % __version__,
                    version = __version__,
                    options_first = True
                )
    
                paths = options.pop('<path>', None)
                setattr(cls, "destination_path", paths.pop() if paths else None)
                setattr(cls, "source_paths", paths)
                setattr(cls, "options", options)
    
                for k, v in options.iteritems():
                    setattr(cls, k, v)
    
                GsyncListOptions._GsyncListOptions__initialised = True
    
            def list(cls):
                return GsyncListOptions
    
            def __getattr__(cls, name):
                cls.__initialiseClass()
                return getattr(GsyncListOptions, name)[-1]
    
            def __setattr__(cls, name, value):
                # Substitut option names: --an-option-name for an_option_name
                import re
                name = re.sub(r'^__', "", re.sub(r'-', "_", name))
                listvalue = []
    
                # Ensure value is converted to a list type for GsyncListOptions
                if isinstance(value, list):
                    if value:
                        listvalue = [] + value
                    else:
                        listvalue = [ None ]
                else:
                    listvalue = [ value ]
    
                type.__setattr__(GsyncListOptions, name, listvalue)
    
        # Cleanup this module to prevent tinkering.
        import sys
        module = sys.modules[__name__]
        del module.__dict__['GetGsyncOptionsType']
    
        return GsyncOptionsType
    
    # Our singlton abstract proxy class.
    class GsyncOptions(object):
        __metaclass__ = GetGsyncOptionsType()
    
        
    50
    2016-01-25 20: 08: 37Z

    type es en realidad un metaclass, una clase que crea otras clases. La mayoría de los metaclass son las subclases de type. El metaclass recibe la clase new como su primer argumento y proporciona acceso al objeto de clase con los detalles que se mencionan a continuación:

     
    >>> class MetaClass(type):
    ...     def __init__(cls, name, bases, attrs):
    ...         print ('class name: %s' %name )
    ...         print ('Defining class %s' %cls)
    ...         print('Bases %s: ' %bases)
    ...         print('Attributes')
    ...         for (name, value) in attrs.items():
    ...             print ('%s :%r' %(name, value))
    ... 
    
    >>> class NewClass(object, metaclass=MetaClass):
    ...    get_choch='dairy'
    ... 
    class name: NewClass
    Bases <class 'object'>: 
    Defining class <class 'NewClass'>
    get_choch :'dairy'
    __module__ :'builtins'
    __qualname__ :'NewClass'
    

    Note:

    Note que la clase no fue instanciada en ningún momento; el simple acto de crear la clase activó la ejecución del metaclass.

        
    36
    2017-08-29 05: 23: 15Z

    La versión tl; dr

    La función type(obj) le da el tipo de un objeto.

    El type() de una clase es su metaclass.

    Para usar una metaclase:

     
    class Foo(object):
        __metaclass__ = MyMetaClass
    
        
    32
    2016-12-30 17: 28: 40Z

    Las clases de Python son, en sí mismas, objetos de su meta-clase.

    La metaclase predeterminada, que se aplica cuando se determinan las clases como:

     
    class foo:
        ...
    

    la meta clase se usa para aplicar alguna regla a un conjunto completo de clases. Por ejemplo, suponga que está creando un ORM para acceder a una base de datos y desea que los registros de cada tabla pertenezcan a una clase asignada a esa tabla (según los campos, las reglas comerciales, etc.), un posible uso de metaclase es, por ejemplo, la lógica de la agrupación de conexiones, que comparten todas las clases de registros de todas las tablas.Otro uso es la lógica para admitir claves externas, que involucra múltiples clases de registros.

    cuando define metaclase, subclasifica su tipo y puede anular los siguientes métodos mágicos para insertar su lógica.

     
    class somemeta(type):
        __new__(mcs, name, bases, clsdict):
          """
      mcs: is the base metaclass, in this case type.
      name: name of the new class, as provided by the user.
      bases: tuple of base classes 
      clsdict: a dictionary containing all methods and attributes defined on class
    
      you must return a class object by invoking the __new__ constructor on the base metaclass. 
     ie: 
        return type.__call__(mcs, name, bases, clsdict).
    
      in the following case:
    
      class foo(baseclass):
            __metaclass__ = somemeta
    
      an_attr = 12
    
      def bar(self):
          ...
    
      @classmethod
      def foo(cls):
          ...
    
          arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}
    
          you can modify any of these values before passing on to type
          """
          return type.__call__(mcs, name, bases, clsdict)
    
    
        def __init__(self, name, bases, clsdict):
          """ 
          called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
          """
          pass
    
    
        def __prepare__():
            """
            returns a dict or something that can be used as a namespace.
            the type will then attach methods and attributes from class definition to it.
    
            call order :
    
            somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
            """
            return dict()
    
        def mymethod(cls):
            """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
            """
            pass
    

    de todos modos, esos dos son los ganchos más utilizados. metaclassing es poderoso, y la lista de usos anteriores para metaclassing no está ni cerca ni lejos.

        
    22
    2017-07-13 08: 18: 22Z

    La función type () puede devolver el tipo de un objeto o crear un nuevo tipo,

    por ejemplo, podemos crear una clase Hi con la función type () y no necesitamos usarla de esta manera con la clase Hi (objeto):

     
    def func(self, name='mike'):
        print('Hi, %s.' % name)
    
    Hi = type('Hi', (object,), dict(hi=func))
    h = Hi()
    h.hi()
    Hi, mike.
    
    type(Hi)
    type
    
    type(h)
    __main__.Hi
    

    Además de usar type () para crear clases dinámicamente, puede controlar el comportamiento de creación de la clase y usar metaclase.

    De acuerdo con el modelo de objetos de Python, la clase es el objeto, por lo que la clase debe ser una instancia de otra clase determinada. De forma predeterminada, una clase de Python es una instancia de la clase de tipo. Es decir, el tipo es metaclase de la mayoría de las clases integradas y la metaclase de clases definidas por el usuario.

     
    class ListMetaclass(type):
        def __new__(cls, name, bases, attrs):
            attrs['add'] = lambda self, value: self.append(value)
            return type.__new__(cls, name, bases, attrs)
    
    class CustomList(list, metaclass=ListMetaclass):
        pass
    
    lst = CustomList()
    lst.add('custom_list_1')
    lst.add('custom_list_2')
    
    lst
    ['custom_list_1', 'custom_list_2']
    

    La magia tendrá efecto cuando pasamos los argumentos de palabras clave en metaclase, esto indica al intérprete de Python para crear la Lista personalizada a través de ListMetaclass. nuevo (), en este punto, podemos modificar la definición de clase, por ejemplo, y agregar un nuevo método y luego devolver la definición revisada.

        
    17
    2018-01-12 09: 30: 20Z

    Además de las respuestas publicadas, puedo decir que un metaclass define el comportamiento de una clase. Por lo tanto, puede establecer explícitamente su metaclase. Cada vez que Python obtiene una palabra clave class, comienza a buscar el metaclass. Si no se encuentra, el tipo de metaclase predeterminado se usa para crear el objeto de la clase. Usando el atributo __metaclass__, puede establecer metaclass de su clase:

     
    class MyClass:
       __metaclass__ = type
       # write here other method
       # write here one more method
    
    print(MyClass.__metaclass__)
    

    Producirá una salida como esta:

     
    class 'type'
    

    Y, por supuesto, puede crear su propio metaclass para definir el comportamiento de cualquier clase que se cree utilizando su clase.

    Para hacer eso, su clase de tipo metaclass predeterminada debe heredarse ya que este es el metaclass principal:

     
    class MyMetaClass(type):
       __metaclass__ = type
       # you can write here any behaviour you want
    
    class MyTestClass:
       __metaclass__ = MyMetaClass
    
    Obj = MyTestClass()
    print(Obj.__metaclass__)
    print(MyMetaClass.__metaclass__)
    

    La salida será:

     
    class '__main__.MyMetaClass'
    class 'type'
    
        
    7
    2018-09-15 13: 17: 29Z
fuente colocada aquí