python metaclass __new__ - Que sont les métaclasses en Python?




8 Answers

Une métaclasse est la classe d'une classe. Comme une classe définit le comportement d'une instance de la classe, une métaclasse définit le comportement d'une classe. Une classe est une instance d'une métaclasse.

Tandis qu'en Python, vous pouvez utiliser des callables arbitraires pour les métaclasses (comme le montre Jerub ), l'approche la plus utile consiste à en faire une classe elle-même. type est la métaclasse habituelle en Python. Si vous vous posez la question, oui, le type est lui-même une classe et son propre type. Vous ne pourrez pas recréer quelque chose comme du type purement en Python, mais Python triche un peu. Pour créer votre propre métaclasse en Python, vous voulez simplement type .

Une métaclasse est le plus souvent utilisée comme classe-factory. Comme vous créez une instance de la classe en appelant la classe, Python crée une nouvelle classe (lorsqu'il exécute l'instruction 'class') en appelant la métaclasse. Combinées aux méthodes normales __init__ et __new__ , les métaclasses vous permettent donc de faire des choses supplémentaires lors de la création d'une classe, comme enregistrer la nouvelle classe avec un registre ou même de remplacer la classe par autre chose.

Lorsque l'instruction de class est exécutée, Python exécute d'abord le corps de l'instruction de class tant que bloc de code normal. L'espace de nommage résultant (un dict) contient les attributs de la classe à venir. La métaclasse est déterminée en examinant les classes de base de la classe à venir (les métaclasses sont héritées), à l'attribut __metaclass__ la __metaclass__ à __metaclass__ (s'il y en a) ou à la variable globale __metaclass__ . La métaclasse est ensuite appelée avec le nom, les bases et les attributs de la classe pour l'instancier.

Cependant, les métaclasses définissent en réalité le type d'une classe, pas seulement une usine, et vous pouvez donc en faire beaucoup plus. Vous pouvez, par exemple, définir des méthodes normales sur la métaclasse. Ces méthodes de métaclasse sont comme des méthodes de classe, en ce sens qu'elles peuvent être appelées sur la classe sans instance, mais elles ne sont pas non plus comme des méthodes de classe car elles ne peuvent pas être appelées sur une instance de la classe. type.__subclasses__() est un exemple de méthode sur la métaclasse de type . Vous pouvez également définir les méthodes «magiques» normales, telles que __add__ , __iter__ et __getattr__ , pour implémenter ou modifier le comportement de la classe.

Voici un exemple agrégé des morceaux:

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__
heritage abstraite

Que sont les métaclasses et à quoi les utilisons-nous?




Notez que cette réponse concerne Python 2.x telle qu’elle a été écrite en 2008, les métaclasses sont légèrement différentes en 3.x, voir les commentaires.

Les métaclasses sont la sauce secrète qui fait le travail de classe. La métaclasse par défaut pour un nouvel objet de style s'appelle 'type'.

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

Les métaclasses prennent 3 arguments. ' name ', ' bases ' et ' dict '

Voici où le secret commence. Cherchez d'où proviennent le nom, les bases et le dict dans cet exemple de définition de classe.

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

Définissons une métaclasse qui montrera comment " classe: " l'appelle.

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'

Et maintenant, un exemple qui signifie quelque chose, les variables de la liste "attributs" seront automatiquement définies dans la classe et définies sur Aucun.

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

Notez que le comportement magique qu'Initalised gagne en ayant la métaclasse init_attributes n'est pas transmis à une sous-classe d'Initalised.

Voici un exemple encore plus concret, montrant comment vous pouvez sous-classe 'type' pour créer une métaclasse qui effectue une action lors de la création de la classe. C'est assez délicat:

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



D'autres ont expliqué le fonctionnement des métaclasses et leur intégration dans le système de types Python. Voici un exemple de ce qu'ils peuvent être utilisés. Dans un framework de test que j'ai écrit, je voulais garder une trace de l'ordre dans lequel les classes étaient définies, afin de pouvoir les instancier ultérieurement dans cet ordre. J'ai trouvé le plus facile de faire cela en utilisant une métaclasse.

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

Tout ce qui est une sous-classe de MyType obtient alors un attribut de classe _order qui enregistre l'ordre dans lequel les classes ont été définies.




Que sont les métaclasses? A quoi les utilisez-vous?

TLDR: une métaclasse instancie et définit le comportement d'une classe de la même manière qu'une classe instancie et définit le comportement d'une instance.

Pseudocode:

>>> Class(...)
instance

Ce qui précède devrait avoir l'air familier. Eh bien, d'où vient la Class ? C'est une instance d'une métaclasse (également un pseudocode):

>>> Metaclass(...)
Class

En code réel, nous pouvons passer la métaclasse par défaut, type , tout ce dont nous avons besoin pour instancier une classe et nous obtenons une classe:

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

En d'autres termes

  • Une classe est à une instance comme une métaclasse est à une classe.

    Lorsque nous instancions un objet, nous obtenons une instance:

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

    De même, lorsque nous définissons explicitement une classe avec la métaclasse par défaut, type , nous l'instancions:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • En d'autres termes, une classe est une instance d'une métaclasse:

    >>> isinstance(object, type)
    True
    
  • En d'autres termes, une métaclasse est une classe.

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

Lorsque vous écrivez une définition de classe et que Python l'exécute, il utilise une métaclasse pour instancier l'objet de classe (qui, à son tour, sera utilisé pour instancier des instances de cette classe).

Tout comme nous pouvons utiliser les définitions de classe pour modifier le comportement des instances d'objet personnalisé, nous pouvons utiliser une définition de classe de métaclasse pour modifier le comportement d'un objet de classe.

Que peuvent-ils être utilisés? De la docs :

Les utilisations potentielles des métaclasses sont illimitées. Parmi les idées explorées figurent la journalisation, la vérification des interfaces, la délégation automatique, la création automatique de propriétés, les mandataires, les infrastructures et le verrouillage / synchronisation automatique des ressources.

Néanmoins, il est généralement recommandé aux utilisateurs d’éviter d’utiliser des métaclasses, sauf en cas de nécessité absolue.

Vous utilisez une métaclasse chaque fois que vous créez une classe:

Lorsque vous écrivez une définition de classe, par exemple, comme ceci,

class Foo(object): 
    'demo'

Vous instanciez un objet de classe.

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

Cela revient à appeler un type fonctionnellement avec les arguments appropriés et à assigner le résultat à une variable de ce nom:

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

Notez que certaines choses sont automatiquement ajoutées à __dict__ , c'est-à-dire l'espace de nom:

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

La métaclasse de l'objet créé, dans les deux cas, est le type .

(Une note de côté sur le contenu de la classe __dict__ : __module__ est présente car les classes doivent savoir où elles sont définies, et __dict__ et __weakref__ sont présentes car nous ne définissons pas __slots__ . espace dans les instances, car nous pouvons interdire __dict__ et __weakref__ en les excluant, par exemple:

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

... mais je m'égare.)

Nous pouvons étendre le type comme n'importe quelle autre définition de classe:

Voici le __repr__ par défaut des classes:

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

L'une des choses les plus utiles que nous puissions faire par défaut en écrivant un objet Python est de lui fournir un bon __repr__ . Lorsque nous appelons help(repr) nous apprenons qu'il existe un bon test pour un __repr__ qui requiert également un test d'égalité - obj == eval(repr(obj)) . L'implémentation simple suivante de __repr__ et __eq__ pour les instances de classe de notre classe type nous fournit une démonstration susceptible d'améliorer la valeur par défaut __repr__ des classes:

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__)

Alors maintenant, lorsque nous créons un objet avec cette métaclasse, l' __repr__écho sur la ligne de commande fournit une vision beaucoup moins laide que celle par défaut:

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

Avec une belle __repr__définition pour l'instance de classe, nous avons une capacité plus grande de déboguer notre code. Cependant, il eval(repr(Class))est peu probable que des vérifications supplémentaires soient effectuées (car les fonctions seraient plutôt impossibles à évaluer depuis leurs valeurs par défaut __repr__).

Une utilisation attendue: __prepare__un espace de noms

Si, par exemple, nous voulons savoir dans quel ordre les méthodes d'une classe sont créées, nous pourrions fournir un dict ordonné en tant qu'espace de nom de la classe. Nous ferions ceci avec __prepare__qui renvoie le dict d'espace de noms pour la classe si elle est implémentée dans 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

Et utilisation:

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

Et maintenant nous avons un enregistrement de l'ordre dans lequel ces méthodes (et autres attributs de classe) ont été créés:

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

Notez que cet exemple a été adapté à partir de la docs - le nouvel enum de la bibliothèque standard le fait.

Nous avons donc instancié une métaclasse en créant une classe. Nous pouvons également traiter la métaclasse comme n'importe quelle autre classe. Il a un ordre de résolution de méthode:

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

Et il a approximativement le correct repr(que nous ne pouvons plus évaluer si nous ne pouvons pas trouver un moyen de représenter nos fonctions.):

>>> 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>})



Rôle d'une __call__()méthode de métaclasse lors de la création d'une instance de classe

Si vous avez programmé Python pendant plus de quelques mois, vous finirez par tomber sur un code qui ressemble à ceci:

# 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')

Ce dernier est possible lorsque vous implémentez la __call__()méthode magique sur la classe.

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

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

La __call__()méthode est appelée lorsqu'une instance d'une classe est utilisée en tant qu'appelable. Mais comme nous l'avons vu à partir de réponses précédentes, une classe elle-même est une instance d'une métaclasse. Par conséquent, lorsque nous utilisons la classe comme appelable (c'est-à-dire lorsque nous en créons une instance), nous appelons en fait sa __call__()méthode de métaclasse . À ce stade, la plupart des programmeurs Python sont un peu confus, car on leur a dit que lorsque vous créez une instance comme celle-ci, instance = SomeClass()vous appelez sa __init__()méthode. Certains qui ont creusé un peu plus profond savent que avant __init__()il y a __new__(). Eh bien, aujourd'hui, une autre couche de vérité est en train d'être révélée, avant __new__()qu'il y ait la métaclasse __call__().

Etudions la chaîne d’appel à la méthode sous l’angle de la création d’une instance de classe.

Il s'agit d'une métaclasse qui enregistre exactement le moment précédant la création d'une instance et le moment où elle est sur le point de la renvoyer.

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

Ceci est une classe qui utilise cette métaclasse

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__()."

Et maintenant, créons une instance 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.

Observez que le code ci-dessus ne fait en réalité rien de plus que la journalisation des tâches. Chaque méthode délègue le travail réel à l'implémentation de son parent, conservant ainsi le comportement par défaut. Étant donné que la classe parent de typeis Meta_1( typeétant la métaclasse parent par défaut) et compte tenu de la séquence de classement du résultat ci-dessus, nous avons maintenant un indice sur ce que serait la pseudo-implémentation 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

Nous pouvons voir que la __call__()méthode de la métaclasse est celle qui est appelée en premier. Il délègue ensuite la création de l'instance à la __new__()méthode de la classe et l'initialisation à celle de l'instance __init__(). C'est aussi celui qui renvoie l'instance.

Il découle de ce qui précède que la métaclasse a __call__()également la possibilité de décider si un appel sera Class_1.__new__()ou Class_1.__init__()sera éventuellement effectué. Au cours de son exécution, il pourrait en fait renvoyer un objet qui n'a été touché par aucune de ces méthodes. Prenons par exemple cette approche du motif 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__()."

Observons ce qui se passe lorsque nous essayons à plusieurs reprises de créer un objet de type 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



typeest en fait une metaclass- une classe qui crée une autre classe. La plupart metaclasssont les sous-classes de type. Le metaclassreçoit la newclasse comme premier argument et fournit un accès à l'objet de classe avec les détails mentionnés ci-dessous:

>>> 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:

Notez que la classe n'a été instanciée à aucun moment; le simple fait de créer la classe a déclenché l’exécution de metaclass.




Les classes Python sont elles-mêmes des objets - comme dans l'exemple - de leur méta-classe.

La métaclasse par défaut, qui est appliquée lorsque vous déterminez des classes en tant que:

class foo:
    ...

Les méta-classes sont utilisées pour appliquer une règle à un ensemble complet de classes. Par exemple, supposons que vous construisez un ORM pour accéder à une base de données et que vous souhaitiez que les enregistrements de chaque table appartiennent à une classe mappée sur cette table (en fonction des champs, des règles métier, etc.,), d'une utilisation possible de la métaclasse. est, par exemple, la logique du pool de connexions, qui est partagée par toutes les classes d’enregistrements de toutes les tables. Une autre utilisation est la logique pour prendre en charge les clés étrangères, qui implique plusieurs classes d'enregistrements.

Lorsque vous définissez une métaclasse, vous sous-classez le type et pouvez remplacer les méthodes magiques suivantes pour insérer votre logique.

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

Quoi qu'il en soit, ces deux crochets sont les crochets les plus couramment utilisés. la méta-classification est puissante, et la liste ci-dessus est loin d’être exhaustive et exhaustive des utilisations de la méta-classification.




En plus des réponses publiées, je peux dire que a metaclassdéfinit le comportement d'une classe. Donc, vous pouvez définir explicitement votre métaclasse. Chaque fois que Python obtient un mot clé, classil commence à rechercher le metaclass. S'il n'est pas trouvé, le type de métaclasse par défaut est utilisé pour créer l'objet de la classe. En utilisant l' __metaclass__attribut, vous pouvez définir metaclassde votre classe:

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

print(MyClass.__metaclass__)

Cela produira la sortie comme ceci:

class 'type'

Et, bien sûr, vous pouvez créer le vôtre metaclasspour définir le comportement de toute classe créée à l'aide de votre classe.

Pour ce faire, votre metaclassclasse de type par défaut doit être héritée, car il s’agit du 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 sortie sera:

class '__main__.MyMetaClass'
class 'type'



Related