Frage

Ich habe einen Freund, den metaclasses verwenden will, und bietet regelmäßig sich als Lösung.

Ich bin der Meinung, dass man fast nie metaclasses verwenden müssen. Warum? weil Ich glaube, wenn man so etwas eine Klasse tun, sollten Sie es wahrscheinlich auf ein Objekt sein zu tun. Und ein kleines Redesign / refactor in Ordnung ist.

In der Lage metaclasses zu verwenden hat viele Menschen in vielen Orten verursacht Klassen als eine Art zweite Rate Objekt zu verwenden, die nur katastrophal mir scheint. Ist die Programmierung durch Meta-Programmierung ersetzt werden? Die Zugabe von Klasse Dekorateure hat leider noch akzeptabel gemacht.

Also bitte, ich bin verzweifelt Ihr gültiges (Beton) Use-Cases für metaclasses in Python kennen. Oder wird aufgeklärt, warum Klassen zu mutieren, ist besser als Objekte mutiert, manchmal.

Ich werde beginnen:

  

Manchmal, wenn unter Verwendung eines Drittanbieters   Bibliothek ist es sinnvoll, in der Lage sein   mutiert die Klasse in einer bestimmten Art und Weise.

(Dies ist der einzige Fall, den ich mir vorstellen kann, und es ist nicht Beton)

War es hilfreich?

Lösung

Ich habe eine Klasse, die nicht-interaktive Plotten behandelt, als Frontend zu Matplotlib. Doch gelegentlich will man interaktives Plot tun. Mit nur ein paar Funktionen fand ich, dass ich in der Lage war, die Zahl Zahl zu erhöhen, rufen Sie manuell zeichnen, etc, aber ich brauchte diese vor und nach jedem Auftragen Aufruf zu tun. So sowohl einen interaktiven Plot-Wrapper zu erstellen und ein virtuellen Plotten Wrapper, fand ich es effizienter war dies über metaclasses zu tun, die entsprechenden Methoden Einwickeln, als wie etwas zu tun:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Diese Methode hält nicht mit API-Änderungen und so weiter, aber eine, die iteriert über die Klasse in __init__ Attribute vor der Wiedereinstellung des Klassenattribut ist effizienter und halten die Dinge auf dem Laufenden:

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

Natürlich könnte es bessere Möglichkeiten, dies zu tun, aber ich habe diese wirksam gefunden. Natürlich ist dies auch in __new__ oder __init__ getan werden könnte, aber das war die Lösung, die ich die einfachste gefunden.

Andere Tipps

Ich habe die gleiche Frage vor kurzem gefragt, und kam mit mehreren Antworten auf. Ich hoffe, dass es OK ist, dieses Thema wieder aufleben zu lassen, wie ich auf ein paar der Anwendungsfälle erwähnt, und fügen Sie ein paar neue erarbeiten wollte.

Die meisten metaclasses ich gesehen habe, eines von zwei Dingen tun:

  1. Anmeldung (eine Klasse auf eine Datenstruktur hinzugefügt):

    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
    

    Wenn Sie Unterklasse Model, Ihre Klasse ist im models Wörterbuch registriert:

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

    Dies kann auch mit Klasse Dekorateure getan werden:

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

    oder mit einer expliziten Registrierung Funktion:

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

    Eigentlich ist dies so ziemlich das gleiche. Sie Klasse Dekorateure ungünstig erwähnen, aber es ist eigentlich nichts anderes als syntaktischer Zucker für einen Funktionsaufruf auf einer Klasse, so gibt es keine Magie über sie

    Wie auch immer, ist der Vorteil des metaclasses in diesem Fall Erbteil, wie sie für alle Unterklassen arbeiten, während die anderen Lösungen nur für Unterklassen arbeiten explizit eingerichtet oder registriert.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. Refactoring (Modifizieren Klassenattribute oder neue hinzufügen):

    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
    

    Wenn Sie Model Unterklasse und definieren einig Field Attribute, werden sie mit ihrem Namen injizieren (für informative Fehlermeldungen, zum Beispiel), und gruppierten in einen _fields Wörterbuch (für die einfache Iteration, ohne durch alle Klassenattribute zu suchen, die und alle seine Basisklassen Attribute jedes Mal):

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

    Auch dies kann (ohne Vererbung) durchgeführt wird mit einer Klasse Dekorateur:

    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')} :(
    

    oder explizit:

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

    Obwohl im Gegensatz zu Ihrem Eintreten für lesbar und wartbar Nicht-Meta-Programmierung, das ist viel umständliche, redundante und fehleranfällige:

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

die am häufigsten verwendeten und konkrete Anwendungsfälle in Betracht gezogen haben, die einzigen Fälle, in denen Sie unbedingt haben metaclasses zu verwenden sind, wenn Sie den Klassennamen oder eine Liste von Basisklassen ändern möchten, weil einmal definiert sind, werden diese Parameter in die Klasse gebacken, und kein Dekorateur oder Funktion können sie 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

Dies kann für die Ausgabe von Warnungen in Frameworks nützlich sein, wenn Klassen mit ähnlichen Namen oder unvollständiger Vererbung Bäumen definiert sind, aber ich kann aus einem Grunde neben Trolling nicht denken, um tatsächlich diese Werte zu ändern. Vielleicht David Beazley kann.

Wie auch immer, in Python 3 metaclasses hat auch die __prepare__ Methode, die Sie die Klasse Körper in eine Mapping andere als ein dict auswerten kann, und unterstützen damit geordnete Attribute, überlasteten Attribute und andere böse cool stuff:

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

Das könnte dir bestellt argumentieren Attribute können mit der Schöpfung Zähler erreicht werden, und Überlastungen können mit Standardargumenten simuliert werden:

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)

Neben viel mehr hässlich, es ist auch weniger flexibel: was, wenn Sie wörtliche Attribute bestellt wollen, wie ganze Zahlen und Strings? Was passiert, wenn None ist ein gültiger Wert für x?

Hier ist ein kreativer Weg, um das erste Problem zu lösen:

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']

Und hier ist ein kreativer Weg, die zweite zu lösen:

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

Aber das ist viel, viel Voodoo-er als ein einfaches metaclass (vor allem die erste, die wirklich Ihr Gehirn schmilzt). Mein Punkt ist, man sich metaclasses als ungewohnt und kontraintuitiv, aber Sie können auch auf sie als nächsten Schritt der Evolution in Programmiersprachen aussehen: Sie müssen nur Ihre Einstellung anpassen. Immerhin könnte man wahrscheinlich alles in C tun, einschließlich einer Struktur mit Funktionszeigern zu definieren und sie als erstes Argument, um ihre Funktionen übergeben. Eine Person zu sehen, C ++ zum ersten Mal könnte man sagen, „was diese Magie ist? Warum ist der Compiler implizit vorbei this Verfahren, aber nicht zu einem regelmäßigen und statischen Funktionen? Es ist daher besser, explizit und ausführliche über Ihre Argumente“Aber dann, objektorientierte Programmierung ist viel mächtiger, wenn Sie es bekommen,.. Und so wird diese, äh ... quasi-aspektorientierte Programmierung, ich denke, Und wenn Sie verstehen metaclasses, sie sind eigentlich sehr einfach, also warum sie nicht benutzen, wenn bequem?

Und schließlich metaclasses sind rad und Programmierung soll Spaß machen. Unter Verwendung von Standardprogrammierkonstrukte und Entwurfsmuster die ganze Zeit ist langweilig und uninspiriert, und behindert Ihre Fantasie. Lebe ein bisschen! Hier ist eine metametaclass, nur für Sie.

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

Der Zweck metaclasses ist nicht die Klasse / Objekt Unterscheidung mit metaclass / Klasse zu ersetzen - es ist das Verhalten der Klassendefinitionen zu ändern (und damit die Instanzen) in gewisser Weise. Effektiv ist es, das Verhalten der Klasse Aussage in einer Weise zu verändern, wenn dies für die jeweilige Domäne als der Standard nützlicher sein kann. Die Dinge, die ich verwendet habe sie für sind:

  • Tracking-Subklassen, in der Regel Handler zu registrieren. Das ist praktisch, wenn ein Plugin Stil Setup verwenden, in dem Sie einfach einen Handler für eine bestimmte Sache registrieren möchten von Subklassen und ein paar Klassenattribute einrichten. z.B. Angenommen, Sie einen Handler für verschiedene Musikformate schreiben, wobei jede Klasse geeignete Methoden (Play / get-Tags usw.) für seinen Typ implementiert. Hinzufügen eines Handlers für eine neue Art wird:

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

    Die metaclass hält dann einen Wörterbuch von {'.mp3' : MP3File, ... } etc, und baut ein Objekt des entsprechenden Typs, wenn Sie einen Handler durch eine Fabrik-Funktion anfordern.

  • Verhalten ändern. Vielleicht möchten Sie eine besondere Bedeutung zu bestimmten Attributen befestigen, in verändertes Verhalten führt, wenn sie vorhanden sind. Zum Beispiel können Sie für Methoden mit dem Namen _get_foo und _set_foo aussehen wollen und sie transparent Eigenschaften umwandeln. Als reales Beispiel, hier ein Rezept, das ich schrieb mehr C-ähnliche Struktur Definitionen zu geben, . Die Metaklasse wird verwendet, um die deklarierten Elemente in einer Struktur Format-String zu konvertieren, Handhabung Vererbung usw., und eine Klasse zu erzeugen, die mit ihm zu tun.

    Für andere reale Beispiele, werfen Sie einen Blick auf verschiedene ORMs, wie sqlalchemy der ORM oder SQLObject . Auch hier ist der Zweck defintions (hier SQL Spaltendefinitionen) mit einer bestimmten Bedeutung zu interpretieren.

Lassen Sie uns mit Tim Peters klassischen Zitat beginnen:

  

Metaklassen sind tiefer Magie als 99%   der Benutzer sollte jemals befürchten. Wenn   Sie fragen, ob Sie brauchen, um sie, Sie   nicht (die Leute, die wirklich brauchen   sie wissen mit Sicherheit, dass sie   brauchen sie, und nicht eine brauchen   Erklärung darüber, warum). Tim Peters   (C.l.p 2002.12.22 post)

Having said that, ich habe (periodisch) über wahre Verwendungen von metaclasses laufen. Derjenige, der in den Sinn kommt, ist in Django, wo alle Ihre Modelle von models.Model erben. models.Model, der wiederum hat einige ernsthafte Magie Ihre DB-Modelle mit Django ORM Güte zu wickeln. Dass Magie geschieht durch metaclasses. Es schafft alle Arten von Ausnahmeklassen, Manager Klassen, etc. etc.

Siehe

django / db / models / base.py, Klasse Modelbase () für den Anfang der Geschichte.

Metaklassen kann für den Bau von domänenspezifischen Sprachen in Python sehr nützlich sein. Konkrete Beispiele sind Django, SQLObject ‚s deklarative Syntax von Datenbankschemata.

Ein einfaches Beispiel von einen Konservativen Metaclass von Ian Bicking:

  

Die metaclasses ich verwendet habe, haben   in erster Linie eine Art zu unterstützen,   deklarativen Programmierstil. Zum   Man betrachte beispielsweise eine Validierung   Schema:

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

Einige andere Techniken: Zutaten für den Aufbau eines DSL in Python (pdf).

Edit (Ali): Ein Beispiel dieser unter Verwendung der Sammlungen und Instanzen zu tun, ist das, was ich würde es vorziehen. Die wichtige Tatsache ist, die Fälle, die Ihnen mehr Macht geben, und beseitigen Grund metaclasses zu verwenden. Weitere erwähnenswert, dass Ihr Beispiel eine Mischung von Klassen und Instanzen verwendet, die sicherlich ein Anzeichen dafür ist, dass Sie nicht einfach tun können, alle mit metaclasses. Und schafft eine wirklich nicht einheitliche Art und Weise tun.

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,])
]

Es ist nicht perfekt, aber schon gibt es fast Null Magie, keine Notwendigkeit für metaclasses und eine verbesserte Gleichmäßigkeit.

Ein vernünftiges Muster von metaclass Gebrauch etwas einmal tun, wenn eine Klasse definiert ist und nicht wiederholt, wann immer die gleiche Klasse instanziiert wird.

Wenn mehrere Klassen das gleiche spezielle Verhalten teilen, __metaclass__=X Wiederholung ist offensichtlich besser als den Spezialcode zu wiederholen und / oder Einführung von Ad-hoc gemeinsame übergeordneten Klassen.

Aber auch mit nur einer Sonderklasse und keine vorhersehbaren Verlängerung, __new__ und __init__ eines metaclass ist ein sauberer Weg Klassenvariablen oder andere globale Daten als Vermischungs Spezial-Code und normale def und class Aussagen in der Klassendefinition Körper zu initialisieren .

Das einzige Mal, dass ich metaclasses in Python verwendet wurde, als ein Wrapper für die Flickr-API zu schreiben.

war mein Ziel Flickrs api Website zu kratzen und dynamisch eine vollständige Klassenhierarchie erzeugen zu ermöglichen API-Zugriff unter Verwendung von Python-Objekten:

# 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

Also in diesem Beispiel, weil ich den ganzen Python Flickr-API von der Website generierte, ich weiß wirklich nicht, die Klassendefinitionen zur Laufzeit. Die Möglichkeit zum dynamischen Typ erzeugen war sehr nützlich.

Ich dachte die gleiche Sache nur gestern und vollständig zustimmen. Die Komplikationen im Code durch Versuche verursacht, um es deklarative Regel der Codebasis schwieriger zu halten, schwerer zu lesen und weniger pythonic meiner Meinung nach. Es auch erfordert in der Regel eine Menge copy.copy () ing (Erbe zu erhalten und von Klasse zu Instanz kopieren) und bedeutet, dass Sie in vielen Orten zu suchen, um zu sehen, was los ist (immer von metaclass up suchen), die gegen das geht auch python Korn. Ich habe formencode und sqlalchemy Code Kommissionierung durch, um zu sehen, ob eine solche deklarative Stil wert war und seine eindeutig nicht. Solcher Stil soll (wie Eigentum und Methoden) und unveränderliche Daten zu Deskriptoren gelassen werden. Ruby hat eine bessere Unterstützung für solche deklarative Stile und ich bin der Kern Python Sprache froh ist, diesen Weg nicht gehen nach unten.

kann ich ihre Verwendung für das Debuggen sehen, eine Metaklasse auf alle Ihre Basisklassen in den reicheren Informationen zu erhalten. Ich sehe ihre Verwendung auch nur in (sehr) großen Projekten loswerden einige Standardcodes zu erhalten (aber über den Verlust der Klarheit). sqlalchemy für Beispiel sie nicht verwendet anderswo , eine bestimmte benutzerdefinierte Methode für alle Unterklassen auf einem Attributwert in der Klassendefinition basiert hinzufügen z.B. ein Spielzeug Beispiel

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

könnte eine Metaklasse haben, das ein Verfahren in dieser Klasse mit besonderen Eigenschaften, basierend auf „Hallo“ (sagen wir eine Methode, die „Hallo“ an das Ende einer Zeichenkette hinzugefügt) erzeugt. Es könnte gut sein für Wartbarkeit um sicherzustellen, dass Sie nicht ein Verfahren in jeder Unterklasse zu schreiben haben du stattdessen alles, was Sie definieren machen ist method_maker_value.

Die Notwendigkeit hierfür ist allerdings so selten und schneidet nur auf ein wenig nach unten einzutippen, dass es nicht wirklich eine Überlegung wert, wenn Sie eine ausreichend große Code-Basis haben.

Sie nie absolut Notwendigkeit eine Metaklasse verwenden, da kann man immer eine Klasse konstruieren, das tut, was Sie Vererbung oder Aggregation der Klasse wollen verwenden Sie ändern möchten.

Wie gesagt, kann es in Smalltalk und Ruby sehr praktisch sein, um eine vorhandene Klasse zu ändern, aber Python mag nicht, dass direkt tun.

Es gibt eine ausgezeichnete Developer Artikel auf metaclassing in Python rel="nofollow das könnte helfen. Die Wikipedia-Artikel ist auch ziemlich gut.

Metaklassen ersetzen nicht Programmierung! Sie sind nur ein Trick, automatisiert oder elegantere einige Aufgaben machen. Ein gutes Beispiel hierfür ist Pygments Syntax-Hervorhebung Bibliothek. Es hat eine Klasse namens RegexLexer, die der Benutzer einen Satz von Regeln lexing als reguläre Ausdrücke für eine Klasse definieren kann. Eine Metaklasse wird verwendet, um die Definitionen zu einem nützlichen Parser zu machen.

Sie sind wie Salz; es ist einfach zu viel zu verwenden.

So wie ich metaclasses verwenden einige Attribute Klassen zur Verfügung zu stellen. Nehmen Sie zum Beispiel:

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

wird setzt das name Attribut für jede Klasse, die die Metaklasse NameClass-zu-Punkt gesetzt haben wird.

Einige GUI-Bibliotheken Probleme haben, wenn mehrere Threads versuchen, mit ihnen zu interagieren. tkinter ist ein solches Beispiel; und während man explizit das Problem mit Ereignissen und Warteschlangen umgehen, kann es viel einfacher sein, die Bibliothek in einer Art und Weise zu verwenden, die das Problem gänzlich ignoriert. Siehe, -. Die Magie der metaclasses

Die Möglichkeit, dynamisch nahtlos eine ganze Bibliothek neu zu schreiben, so dass es richtig funktioniert wie in einer Multi-Thread-Anwendung erwarten kann unter Umständen sehr hilfreich sein. Die safetkinter Modul macht das mit Hilfe eines metaclass durch die threadbox Modul - Ereignisse und Warteschlangen nicht erforderlich

Ein ordentlich Aspekt threadbox ist, dass es nicht das, was Klasse es Klone schert. Es ist ein Beispiel dafür, wie alle Basisklassen können durch eine Metaklasse berührt werden, wenn nötig. Ein weiterer Vorteil, der mit metaclasses kommt, ist, dass sie auf Vererbungsklassen laufen auch. Programme, die sich selbst schreiben - warum nicht?

Der einzige legitime Anwendungsfall eines Metaklasse ist andere nosey Entwickler zu halten aus dem Code zu berühren. Sobald ein neugieriger Entwickler Meister metaclasses und beginnt mit Ihnen stochern, werfen in einer anderen Ebene oder zwei ihnen fern zu halten. Wenn das nicht funktioniert, starten Sie eine rekursive metaclass mit Verwendung type.__new__ oder vielleicht eine Regelung.

(geschrieben Zunge in der Wange, aber ich habe diese Art von Verschleierung getan gesehen. Django ist ein perfektes Beispiel)

Dies ist eine geringfügige Verwendung, aber ... eine Sache, die ich gefunden habe metaclasses nützlich, um eine Funktion aufzurufen ist, wenn eine Unterklasse erstellt wird. Ich kodifiziert diese in eine Metaklasse, die für ein __initsubclass__ Attribut sucht: immer dann, wenn eine Unterklasse erstellt wird, werden alle übergeordneten Klassen, die diese Methode definieren, werden mit __initsubclass__(cls, subcls) aufgerufen. Dies ermöglicht die Erstellung einer übergeordneten Klasse, die dann alle Unterklassen mit einigen globalen Registry registriert, invariant Kontrollen auf Unterklassen ausgeführt wird, wenn sie definiert sind, führen Operationen späte Bindung, etc ... alles ohne manuell müssen rufen Funktionen oder , um benutzerdefinierte metaclasses zu erstellen, die jede dieser einzelnen Aufgaben zu erfüllen.

Wohlgemerkt, ich langsam gekommen, die implizite magicalness dieses Verhaltens zu erkennen, ist etwas unerwünscht, da es unerwartet ist, wenn bei einer Klassendefinition sucht aus dem Zusammenhang gerissen ... und so habe ich weggezogen aus, dass die Lösung unter Verwendung von für etwas Ernstes neben einem __super Attribut für jede Klasse und Instanz initialisiert wird.

Ich hatte vor kurzem eine Metaklasse verwenden deklarativ definieren eine SQLAlchemy Modell um eine Datenbanktabelle mit US Census Daten von http://census.ire.org/data/bulkdata.html

IRE bietet Datenbank Schalen für die Volkszählungsdaten Tabellen , das erstellen integer-Spalt nach einer Namenskonvention aus dem Census Bureau of p012015, p012016, p012017, etc.

ich einen gesuchter) in der Lage sein, diese Spalten zugreifen, um eine model_instance.p012017 Syntax, b) seine ziemlich explizit über das, was ich tat, und c) nicht explizit zu Dutzenden von Feldern auf dem Modell definieren, so dass ich subclassed SQLAlchemy des DeclarativeMeta zu durchlaufen eine Reihe von Spalten und erstellen automatisch Felder Modell zu den Spalten entsprechen:

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

Ich kann dann diesen Metaklasse für meine Modelldefinition verwenden und die automatisch aufgezählten Felder auf dem Modell zugreifen:

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

    ...

Es scheint eine legitime Anwendung beschrieben werden hier - Umschreiben von Python Docstrings mit einem Metaclass

.

Ich hatte sie für einen binären Parser einmal verwenden, um es einfacher zu bedienen. Sie definieren eine Nachrichtenklasse mit Attributen der Felder, die auf dem Draht. Sie mußten in der Art und Weise bestellt werden sie das endgültige Draht Format daraus zu konstruieren deklariert wurden. Sie können das tun mit metaclasses, wenn Sie einen geordneten Namensraum dict verwenden. In der Tat, sein in den Beispielen für Metaklassen:

https://docs.python.org/3/reference/ datamodel.html # metaclass-Beispiel

Aber im Allgemeinen. Sehr sorgfältig prüfen, ob Sie wirklich, wirklich die zusätzliche Komplexität von Metaklassen müssen

die Antwort von @ Dan Gittik ist cool

die Beispiele am Ende können viele Dinge klären, habe ich es auf Python 3 und eine Erklärung geben:

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

  • alles ist Objekt, so Klasse ist Objekt
  • Klassenobjekt wird durch Metaklasse erstellt
  • alle Klassen von Typ inheritted ist metaclass
  • metaclass konnte steuern Klasse erstellen
  • könnte metaclass steuern metaclass auch zu schaffen (so könnte es Schleife immer)
  • dies ist metaprograming ... Sie den Typ System bei Laufzeit kontrollieren können
  • wieder, alles ist Objekt, das ist ein einheitliches System zu erstellen, geben Typ, und geben Sie erstellen Instanz

Ein weiterer Anwendungsfall ist, wenn Sie auf Klassenebene Attribute in der Lage sein wollen, zu ändern und sicher sein, dass es nur um das Objekt in der Hand beeinflusst. In der Praxis bedeutet dies, „Verschmelzung“ die Phasen der metaclasses und Klassen instantiations, so führt man nur mit Klasseninstanzen ihrer eigenen (einzigartig) Art zu beschäftigen.

Ich hatte auch zu tun, dass, wenn (für Belange der bessere Lesbarkeit und Polymorphismus ) wir auf wollte dynamisch definieren property s, die aus Berechnungen ergeben zurückgegebenen Werte (können), basierend auf ( oft zu ändern) Instanzebene Attribute, die kann nur auf Klassenebene dh nach der metaclass Instanziierung und vor der Klasse Instanziierung erfolgen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top