python __getitem__ __dict__ - ¿Qué son las metaclases en Python?




8 Answers

Una metaclase es la clase de una clase. Al igual que una clase define cómo se comporta una instancia de la clase, una metaclase define cómo se comporta una clase. Una clase es una instancia de una metaclase.

Mientras que en Python puede usar Jerub arbitrarios para las metaclases (como Jerub muestra Jerub ), el enfoque más útil es en realidad convertirlo en una clase en sí. type es la metaclase habitual en Python. En caso de que se lo pregunte, sí, el type es en sí mismo una clase, y es su propio tipo. No podrás recrear algo como el type puramente en Python, pero Python hace un poco de trampa. Para crear tu propia metaclase en Python, solo quieres subclasificar.

Una metaclase se usa más comúnmente como una clase de fábrica. Al igual que creas una instancia de la clase llamando a la clase, Python crea una nueva clase (cuando ejecuta la declaración de 'clase') llamando a la metaclase. Combinadas con los __init__ 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 incluso reemplazar la clase con algo completamente distinto.

Cuando se ejecuta la instrucción de class , Python ejecuta primero el cuerpo de la instrucción de 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 observando las clases de base 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 ella, así que puedes hacer mucho más con ellas. Puede, por ejemplo, definir métodos normales en la metaclase. Estos métodos de metaclase son como los métodos de clase, ya que pueden invocarse en la clase sin una instancia, pero tampoco son como los métodos de clase porque no pueden invocarse en una instancia de la clase. type.__subclasses__() es un ejemplo de un método en el type metaclass. 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__
__hash__ __eq__

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




Tenga en cuenta que esta respuesta es para Python 2.x como se escribió en 2008, las metaclases son ligeramente diferentes en 3.x, vea los comentarios.

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 'Inicializado' gana al tener los init_attributes metaclase no se pasa a una subclase de Initalizado.

Aquí hay 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



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.




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

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

Pseudocódigo

>>> Class(...)
instance

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

>>> Metaclass(...)
Class

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

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

Poniéndolo diferente

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

    Cuando instanciamos 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, tipeamos, creamos una instancia:

    >>> 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 manera, una metaclase es una clase de clase.

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

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

Al igual que 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 el comportamiento de un objeto de clase.

¿Para qué se pueden usar? De la 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.

Usas una metaclase cada vez que creas 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 a __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 de type .

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

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

... pero yo divago.)

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

Aquí está el __repr__ predeterminado de las 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 una buena __repr__ . Cuando llamamos help(repr) , aprendemos que hay una buena prueba para una __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 las 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__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, eval(repr(Class))es poco probable que se realicen muchas más comprobaciones (ya que las funciones serían más bien imposibles de evaluar a partir de sus valores predeterminados __repr__).

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 lo __prepare__que devuelve el espacio de nombres dict para la 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')

Tenga en cuenta que este ejemplo se adaptó de la docs : la nueva enumeración en la biblioteca estándar lo hace.

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 lo correcto repr(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>})



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

Si ha realizado la programación en Python durante más de unos pocos meses, eventualmente se encontrará 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 __call__()método mágico en la clase.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

El __call__()método se invoca cuando una instancia de una clase se utiliza como una llamada. Pero como hemos visto en respuestas anteriores, una clase en sí misma es una instancia de una metaclase, por lo que cuando usamos la clase como llamable (es decir, cuando creamos una instancia de ella), en realidad estamos llamando al __call__()método de metaclase . En este punto, la mayoría de los programadores de Python están un poco confundidos porque se les ha dicho que cuando crean una instancia como esta instance = SomeClass(), están llamando a su __init__()método. Algunos que han cavado un poco más profundo saben que antes __init__()hay __new__(). Bueno, hoy se está revelando otra capa de verdad, antes de que __new__()exista 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 anterior a la creación de 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 typees Meta_1la clase padre ( typees la metaclase padre predeterminada) y teniendo en cuenta 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 __call__()método de metaclase es el que se llama primero. Luego delega la creación de la instancia al __new__()método de la clase y la inicialización a la instancia __init__(). También es el que finalmente devuelve la instancia.

De lo anterior se desprende que a la metaclase __call__()también se le da la oportunidad de decidir si se realizará o no una llamada Class_1.__new__()o Class_1.__init__()finalmente se realizará. 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 para el patrón de 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 qué sucede cuando intentamos crear un objeto de tipo repetidamente. 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



typees en realidad una metaclassclase que crea otras clases. La mayoría metaclassson las subclases de type. El metaclassrecibe la newclase 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 desencadenó la ejecución del metaclass.




Las clases de Python son en sí mismas objetos, como en la instancia, 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 sean de 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 es compartida por todas las clases de registro 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 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.




Además de las respuestas publicadas, puedo decir que a metaclassdefine 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 __metaclass__atributo, puedes establecer metaclassde tu clase:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

Producirá la salida de esta manera:

class 'type'

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

Para hacer eso, su metaclassclase de tipo predeterminada debe ser heredada ya que esta es la principal metaclass:

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'



Related