Question

J'ai un ami qui aime utiliser métaclasses, et leur offre régulièrement une solution.

Je suis de l'esprit que vous avez besoin presque jamais utiliser métaclasses. Pourquoi? parce que je suppose que si vous faites quelque chose comme ça à une classe, vous devriez probablement le faire à un objet. Et une petite nouvelle conception / refactoring est en ordre.

Etre capable d'utiliser métaclasses a causé beaucoup de gens dans beaucoup d'endroits à utiliser les classes comme une sorte de second objet de taux, ce qui semble juste désastreux pour moi. La programmation est à remplacer par des méta-programmation? L'ajout de décorateurs de classe a malheureusement rendu encore plus acceptable.

Alors s'il vous plaît, je suis désespéré de connaître vos valides (concrètes) cas d'utilisation pour métaclasses en Python. Ou, pour être éclairé sur les raisons pour lesquelles les classes est mieux muter que les objets muter, parfois.

Je vais commencer:

  

Parfois, lorsque vous utilisez un tiers   bibliothèque, il est utile de pouvoir   muter la classe d'une certaine façon.

(Ceci est le seul cas que je peux penser, et ce n'est pas concret)

Était-ce utile?

La solution

J'ai une classe qui gère les non-interactif tracé, comme frontend à Matplotlib. Cependant, à l'occasion on veut faire interactive tracé. Avec seulement un couple fonctionne, je trouve que j'ai pu augmenter le nombre de chiffres, appelez dessiner manuellement, etc, mais je devais faire ces avant et après chaque appel de traçage. Donc, pour créer à la fois une enveloppe traçante interactive et une enveloppe de traçage offscreen, j'ai trouvé qu'il était plus efficace de le faire via métaclasses, enveloppant les méthodes appropriées, que de faire quelque chose comme:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Cette méthode ne tient pas avec les changements de l'API et ainsi de suite, mais qui effectue une itération sur la classe des attributs dans __init__ avant réinitialisant les attributs de classe est plus efficace et maintient les choses à ce jour:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

Bien sûr, il pourrait y avoir de meilleures façons de le faire, mais j'ai trouvé que cela soit efficace. Bien sûr, cela pourrait aussi se faire dans __new__ ou __init__, mais c'était la solution que je trouve le plus simple.

Autres conseils

On m'a demandé récemment la même question, et est venu avec plusieurs réponses. J'espère que ce sera OK pour faire revivre ce fil, comme je voulais évoquer plusieurs des cas d'utilisation mentionnées, et ajouter quelques nouvelles.

La plupart des méta-classes que j'ai vu faire une de deux choses:

  1. Inscription (ajout d'une classe à une structure de données):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Chaque fois que vous sous-classe Model, votre classe est inscrit dans le dictionnaire models:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    Cela peut aussi se faire avec des décorateurs de classe:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    Ou avec une fonction d'enregistrement explicite:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    En fait, cela est à peu près la même chose:. Vous mentionnez décorateurs de classe défavorable, mais il est vraiment rien de plus que le sucre syntaxique pour un appel de fonction sur une classe, donc il n'y a pas de magique

    Quoi qu'il en soit, l'avantage de métaclasses dans ce cas est l'héritage, car ils travaillent pour des sous-classes, alors que les autres solutions ne fonctionnent que pour les sous-classes explicitement décorées ou enregistrés.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. Refactoring (modification des attributs de la classe ou l'ajout de nouveaux):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Chaque fois que vous sous-classe Model et définir certains attributs Field, ils sont injectés avec leurs noms (pour les messages d'erreur plus d'information, par exemple), et regroupées dans un dictionnaire _fields (pour l'itération facile, sans avoir à regarder à travers tous les attributs de classe et toutes ses classes de base des attributs à chaque fois):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    Encore une fois, cela peut être fait (sans héritage) avec un décorateur de classe:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    Ou explicitement:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    Bien que, au contraire à votre plaidoyer pour la programmation lisible et maintenable non-méta, ce qui est beaucoup plus lourd, redondant et sujette aux erreurs:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

Ayant examiné les cas d'utilisation les plus communs et concrets, les seuls cas où vous devez absolument utiliser métaclasses sont quand vous voulez modifier le nom de la classe ou de la liste des classes de base, car une fois définis, ces paramètres sont cuits dans la classe, et aucun décorateur ou fonction peut les unbake.

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

Cela peut être utile dans le cadre de l'émission d'avertissements à chaque fois que les classes avec des noms similaires ou des arbres d'héritage incomplets sont définis, mais je ne peux pas penser à une raison à côté de pêche à la traîne de réellement changer ces valeurs. Peut-être peut David Beazley.

Quoi qu'il en soit, en Python 3, métaclasses ont également la méthode __prepare__, qui vous permet d'évaluer le corps de la classe dans une application autre qu'un dict, soutenant ainsi les attributs ordonnés, attributs surchargées, et d'autres trucs cool méchant:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

Vous pourriez faire valoir les attributs commandés peuvent être obtenus avec des compteurs de création, et la surcharge peuvent être simulés avec des arguments par défaut:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

En plus d'être beaucoup plus laid, il est aussi moins souple: si vous voulez des attributs ordonnés littérales, comme les entiers et les chaînes? Que faire si None est une valeur valide pour x?

Voici une façon créative pour résoudre le premier problème:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

Et voici une façon créative pour résoudre le second:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

Mais ce qui est beaucoup, beaucoup voodoo-er qu'un simple métaclasse (en particulier le premier, qui fond vraiment votre cerveau). Mon point est, vous regardez métaclasses comme inconnu et contre-intuitif, mais vous pouvez aussi les regarder comme la prochaine étape de l'évolution des langages de programmation: il vous suffit de régler votre état d'esprit. Après tout, vous pourriez probablement faire tout en C, y compris la définition d'une structure avec des pointeurs de fonction et de la transmettre comme premier argument à ses fonctions. Une personne voyant C ++ peut dire pour la première fois, « ce qui est cette magie? Pourquoi le compilateur passe implicitement this méthodes, mais pas aux fonctions régulières et statiques? Il est préférable d'être explicite et bavard au sujet de vos arguments » Mais alors, la programmation orientée objet est beaucoup plus puissant une fois que vous l'obtenez,.. Et donc est-ce, euh ... la programmation quasi-aspect orienté, je suppose Et une fois que vous comprendre métaclasses, ils sont en fait très simple, alors pourquoi ne pas les utiliser au moment opportun?

Et enfin, méta-classes sont rad, et la programmation devrait être amusant. En utilisant des constructions de programmation standard et des modèles de conception tout le temps est ennuyeux et sans intérêt, et entrave votre imagination. Vis un peu! Voici un metametaclass, juste pour vous.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

Le but de métaclasses est de ne pas remplacer la distinction classe / objet avec métaclasse / classe - il est de changer le comportement des définitions de classe (et donc leurs instances) d'une certaine façon. En effet, il est de modifier le comportement de la déclaration de la classe d'une manière qui peut être plus utile pour votre domaine particulier que la valeur par défaut. Les choses que je les ai utilisés pour sont:

  • sous-classes de suivi, généralement d'enregistrer des gestionnaires. Ceci est pratique lorsque vous utilisez une configuration de style plug-in, où vous souhaitez enregistrer un gestionnaire pour une chose particulière simplement par le sous-classement et la mise en place quelques attributs de classe. par exemple. supposons que vous écrivez un gestionnaire pour différents formats de musique, où chaque classe implémente des méthodes appropriées (lecture / etc) Récupérer les tags pour son type. Ajout d'un gestionnaire pour un nouveau type devient:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    Le métaclasse maintient ensuite un dictionnaire de {'.mp3' : MP3File, ... } etc, et construit un objet du type approprié lorsque vous demandez un gestionnaire par une fonction d'usine.

  • Le changement de comportement. Vous pouvez attacher une signification particulière à certains attributs, ce qui entraîne des troubles du comportement quand ils sont présents. Par exemple, vous pouvez rechercher des méthodes avec le nom _get_foo et _set_foo et de les convertir de façon transparente aux propriétés. A titre d'exemple dans le monde réel, est une recette que j'ai écrit pour donner plus de définitions struct C-like . La méta-classe est utilisé pour convertir les éléments déclarés dans une chaîne de format struct, l'héritage de manipulation etc, et produire une classe capable de faire face.

    Pour d'autres exemples du monde réel, un coup d'oeil à divers ORM, comme ORM sqlalchemy ou sqlobject . Encore une fois, le but est d'interpréter DEFINITIONS (ici des définitions de colonne SQL) avec une signification particulière.

Commençons par citation classique de Tim Peter:

  

Métaclasses sont magiques plus profondes que 99%   des utilisateurs ne devrait jamais se soucier. Si   vous vous demandez si vous en avez besoin, vous   ne pas (les personnes qui ont besoin réellement   leur savoir avec certitude qu'ils   besoin d'eux, et ne pas besoin d'un   explication sur les raisons). Tim Peters   (C.l.p poster 22/12/2002)

Cela dit, j'ai (périodiquement) courir à travers vrais usages de métaclasses. Celui qui vient à l'esprit est dans Django où tous vos modèles héritent de models.Model. models.Model, à son tour, fait un peu de magie sérieuse pour envelopper vos modèles DB avec la bonté ORM de Django. Cette magie se produit par des métaclasses. Il crée toutes sortes de classes d'exception, des cours de gestion, etc., etc.

Voir django / db / modèles / base.py, ModelBase () de la classe pour le début de l'histoire.

Métaclasses peut être pratique pour la construction des langues spécifiques de domaine en Python. Des exemples concrets sont Django, la syntaxe déclarative de base de données schèmes de SQLObject.

Un exemple de base de une métaclasse conservateur par Ian Bicking:

  

Les métaclasses que j'ai utilisé ont été   principalement pour soutenir une sorte de   style déclaratif de la programmation. Pour   par exemple, envisager une validation   schéma:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

D'autres techniques: Ingrédients pour construire un DSL en Python (pdf).

Edit (par Ali): Un exemple de faire ces collections et instances à l'aide est ce que je préférerais. Le fait important est le cas, ce qui vous donnent plus de puissance et d'éliminer raison d'utiliser métaclasses. En outre intéressant de noter que votre exemple utilise un mélange de classes et instances, ce qui est certainement une indication que vous ne pouvez pas le faire tout cela avec métaclasses. Et crée une façon vraiment non uniforme de le faire.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

Il est pas parfait, mais il y a déjà presque nulle magie, pas besoin de métaclasses et d'améliorer l'uniformité.

Un motif raisonnable d'utilisation metaclass fait quelque chose une fois quand une classe est définie plutôt que de façon répétée chaque fois que la même classe est instancié.

Lorsque plusieurs classes partagent le même comportement spécial, en répétant __metaclass__=X est évidemment mieux que répéter le code à usage spécial et / ou l'introduction superclasses partagée ad hoc.

Mais même avec une seule classe spéciale et aucune extension prévisible, __new__ et __init__ d'un métaclasse sont un produit de nettoyage moyen pour initialiser les variables de classe ou d'autres données globales que entremêlant le code à usage spécial et les déclarations de def et class normales dans le corps de définition de classe .

La seule fois où je métaclasses en Python était lors de l'écriture d'un wrapper pour l'API Flickr.

Mon but était de gratter site APIs flickr et générer dynamiquement une hiérarchie de classes complète pour permettre accès API à l'aide d'objets Python:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

Ainsi, dans cet exemple, parce que je l'ensemble de l'API généré Python Flickr à partir du site, je ne sais vraiment pas les définitions de classe lors de l'exécution. Être capable de générer dynamiquement des types a été très utile.

Je pensais la même chose plus tard qu'hier et complètement d'accord. Les complications du code causées par des tentatives de le rendre plus déclarative font généralement la base de code plus difficile à maintenir, plus difficile à lire et moins pythonique à mon avis. Il a également habituellement beaucoup de copy.copy () uant (pour maintenir l'héritage et de copier de la classe à l'instance) et signifie que vous devez regarder dans de nombreux endroits pour voir ce qui se passe (toujours à la recherche de métaclasse up) qui va à l'encontre de la grain de python aussi. Je suis fouillant dans le code formencode et sqlalchemy pour voir si un tel style déclaratif en valait la peine et son pas clairement. Un tel style doit être laissée aux descripteurs (tels que les biens et les méthodes) et les données immuables. Ruby a un meilleur support pour ces styles déclaratives et je suis heureux que le langage python de base ne va pas dans cette voie.

Je peux voir leur utilisation pour le débogage, ajoutez une métaclasse à toutes vos classes de base pour obtenir des informations plus riches. Je vois aussi leur utilisation que dans (très) grands projets pour se débarrasser de certains code passe-partout (mais à la perte de clarté). sqlalchemy ne les utiliser ailleurs , d'ajouter une méthode personnalisée particulière à toutes les sous-classes basées sur une valeur d'attribut dans leur définition de classe par exemple un exemple de jouet

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

pourrait avoir une métaclasse qui a généré une méthode dans cette classe avec des propriétés spéciales basées sur « bonjour » (par exemple une méthode qui a ajouté: « bonjour » à la fin d'une chaîne). Il pourrait être bon pour la maintenabilité pour vous assurer ne pas avoir à écrire une méthode dans chaque sous-classe que vous faites place tout ce que vous devez définir est method_maker_value.

La nécessité est si rare que et ne coupe que sur un peu de taper que son pas vraiment à considérer, sauf si vous avez une assez grande base de code.

Vous jamais absolument besoin pour utiliser une métaclasse, puisque vous pouvez toujours construire une classe qui fait ce que vous voulez utiliser l'héritage ou l'agrégation de la classe que vous souhaitez modifier.

Cela dit, il peut être très pratique dans Smalltalk et Ruby pour être en mesure de modifier une classe existante, mais Python n'aime pas faire directement.

Il y a un excellent DeveloperWorks article sur metaclassing en Python qui pourrait aider. Wikipedia article est également très bon.

Métaclasses ne remplacent pas la programmation! Ils sont juste un truc qui permet d'automatiser certaines tâches ou faire plus élégantes. Un bon exemple est Pygments syntaxe bibliothèque mettant en lumière. Il a une classe appelée RegexLexer qui permet à l'utilisateur de définir un ensemble de règles de Lexing comme des expressions régulières sur une classe. Une métaclasse est utilisée pour activer les définitions dans un analyseur utile.

Ils sont comme le sel; il est facile d'utiliser trop.

La façon dont je métaclasses était de fournir des attributs aux classes. Prenons par exemple:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

va mettre l'attribut nom sur chaque classe qui aura le métaclasse mis au point à NameClass.

Certaines bibliothèques GUI ont du mal lorsque plusieurs threads tentent d'interagir avec eux. tkinter est un exemple; et si l'on peut explicitement gérer le problème avec les événements et les files d'attente, il peut être beaucoup plus simple à utiliser la bibliothèque d'une manière qui ne tient pas compte du problème tout à fait. Voici -. La magie des métaclasses

Être capable de réécrire dynamiquement une bibliothèque entière de façon transparente pour qu'il fonctionne correctement comme prévu dans une application multithread peut être extrêmement utile dans certaines circonstances. Le le module safetkinter fait que, avec l'aide d'une métaclasse fournie par le threadbox modules - les événements et les files d'attente ne sont pas nécessaires

Un aspect soigné de threadbox est qu'il ne se soucie pas de quelle classe il clones. Il donne un exemple de la façon dont toutes les classes de base peuvent être touchés par une métaclasse si nécessaire. Un autre avantage qui vient avec métaclasses est qu'ils fonctionnent sur les classes héritant ainsi. Les programmes qui écrivent eux-mêmes - pourquoi pas?

La seule cas d'utilisation légitime d'une métaclasse est de garder les autres développeurs fouineurs de toucher votre code. Une fois une maîtrise des développeurs fouineurs métaclasses et commence à fouiller avec le vôtre, jeter un autre niveau ou deux pour les garder hors. Si cela ne fonctionne pas, commencez à utiliser type.__new__ ou peut-être un système utilisant une métaclasse récursive.

(langue écrite dans la joue, mais je l'ai vu ce genre de faux-fuyants fait. Django est un parfait exemple)

Ceci est une utilisation mineure, mais ... une chose que j'ai trouvé métaclasses utile est d'appeler une fonction à chaque fois une sous-classe est créée. Je codifié cela en une métaclasse qui recherche un attribut __initsubclass__: chaque fois qu'une sous-classe est créé, toutes les classes de parents qui définissent cette méthode sont invoquées avec __initsubclass__(cls, subcls). Cela permet la création d'une classe mère qui enregistre alors toutes les sous-classes avec un certain registre global, exécute des contrôles invariantes sur les sous-classes chaque fois qu'ils sont définis, effectuer des opérations de fin de reliure, etc ... le tout sans avoir à appeler manuellement les fonctions ou pour créer métaclasses personnalisés qui exécutent chacune de ces fonctions distinctes.

Rappelez-vous, je me suis lentement pris conscience de l'magicalness implicite de ce comportement est un peu indésirable, car il est inattendu si vous regardiez à une définition de classe hors contexte ... et donc je me suis éloigné d'utiliser cette solution pour quelque chose de sérieux en plus d'initialiser un attribut __super pour chaque classe et d'instance.

J'ai eu récemment d'utiliser une méta-classe pour aider déclarative définir un modèle SQLAlchemy autour d'une table de base de données avec des données du recensement des États-Unis http://census.ire.org/data/bulkdata.html

IRE fournit des coquilles de base de données rel="nofollow"> pour les tables de données de recensement, qui créent des colonnes entières suite à une convention de nommage du Bureau du recensement des p012015, p012016, p012017, etc.

Je voulais un) pouvoir accéder à ces colonnes en utilisant une syntaxe model_instance.p012017, b) être assez explicite sur ce que je faisais et c) ne sont pas explicitement définir des dizaines de champs sur le modèle, donc je sous-classé de DeclarativeMeta de SQLAlchemy à itérer à travers une série de colonnes et de créer automatiquement les champs du modèle correspondant aux colonnes:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

Je pourrais alors utiliser cette métaclasse pour ma définition du modèle et accéder aux champs énumérés automatiquement sur le modèle:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...

Il semble y avoir une utilisation légitime décrit ici - Réécriture Python docstrings avec métaclasse

.

Je devais les utiliser une fois pour un analyseur binaire pour le rendre plus facile à utiliser. Vous définissez une classe de message avec des attributs des champs présents sur le fil. Ils avaient besoin d'être commandés dans la façon dont ils ont été déclarés pour construire le format de fil final de celui-ci. Vous pouvez le faire avec métaclasses, si vous utilisez un dict espace de noms commandé. En fait, son dans les exemples Métaclasses:

https://docs.python.org/3/reference/ datamodel.html # métaclasse-exemple

Mais en général. Très bien évaluer, si vous avez vraiment vraiment besoin de la complexité supplémentaire des métaclasses

la réponse de Gittik est cool @ Dan

les exemples à la fin pourrait clarifier beaucoup de choses, je l'ai changé à python 3 et donner quelques explications:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

  • tout est objet, si la classe est l'objet
  • objet de classe est créé par métaclasse
  • toutes les classes inheritted de type est métaclasse
  • métaclasse pourrait contrôler la création de la classe
  • métaclasse pourrait contrôler métaclasse créer trop (il boucle pourrait pour toujours)
  • c'est ... vous pouvez méta-programmation contrôler le système de type à durée
  • encore une fois, tout est objet, c'est un système uniforme, tapez créer le type et tapez créer une instance

Un autre cas d'utilisation est quand vous voulez être en mesure de modifier les attributs de niveau de classe et assurez-vous qu'il ne touche que l'objet à portée de main. Dans la pratique, cela implique « fusion » les phases de méta-classes et des classes instanciations, vous conduisant ainsi à traiter uniquement avec des instances de classe de leurs propres (unique) genre.

Je devais aussi faire quand (pour les problèmes de et readibility polymorphisme ) nous voulions définir dynamiquement property s qui ont retourné des valeurs (peut-être) résultent de calculs basés sur ( souvent changeant) attributs niveau de l'instance, qui ne peut être fait au niveau de la classe , ie après l'instanciation metaclass et avant l'instanciation de classe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top