Frage

Lassen Sie uns sagen, dass wir eine Metaklasse CallableWrappingMeta haben, die den Körper einer neuen Klasse geht, ihre Methoden mit einer Klasse Verpackung, InstanceMethodWrapper:

import types

class CallableWrappingMeta(type):
    def __new__(mcls, name, bases, cls_dict):
        for k, v in cls_dict.iteritems():
            if isinstance(v, types.FunctionType):
                cls_dict[k] = InstanceMethodWrapper(v)
        return type.__new__(mcls, name, bases, cls_dict)

class InstanceMethodWrapper(object):
    def __init__(self, method):
        self.method = method
    def __call__(self, *args, **kw):
        print "InstanceMethodWrapper.__call__( %s, *%r, **%r )" % (self, args, kw)
        return self.method(*args, **kw)

class Bar(object):
    __metaclass__ = CallableWrappingMeta
    def __init__(self):
        print 'bar!'

Unser Dummy-Wrapper druckt nur die Argumente, wie sie hereinkommen Aber Sie werden auffällig etwas bemerken. Die Methode nicht die Instanz-Objekt-Empfänger übergeben wird, denn auch wenn InstanceMethodWrapper aufrufbar ist, ist es nicht als Funktion behandelt der Zweck während des Unterrichts Schaffung einer Instanz Verfahren umgewandelt werden (nach unserer Metaklasse ist mit ihr geschehen).

Eine mögliche Lösung ist es, einen Dekorateur statt einer Klasse zu verwenden, die Methoden zu wickeln - diese Funktion wird eine Instanz Methode. Aber in der realen Welt, ist InstanceMethodWrapper viel komplexer: es stellt eine API und veröffentlicht Methode-Aufruf Ereignisse. Eine Klasse ist bequemer (und performante, nicht, dass diese Angelegenheiten viel).

Ich habe auch versucht, einige Sackgassen. Subclassing types.MethodType und types.UnboundMethodType nicht überall hin. Ein wenig Selbstbeobachtung, und es scheint, dass sie von type decend. Also habe ich versucht, sowohl als metaclass verwenden, aber kein Glück da. Es könnte der Fall sein, dass sie besondere Anforderungen als metaclass haben, aber es scheint, dass wir in undokumentierten Gebiet an dieser Stelle sind.

Irgendwelche Ideen?

War es hilfreich?

Lösung

Just bereichern Sie InstanceMethodWrapper Klasse mit einem __get__ (das perfekt kann auch nur return self) - das heißt, die Klasse machen in eine Deskriptor Typ, so dass ihre Instanzen sind Descriptor Objekte. Siehe http://users.rcn.com/python/download/Descriptor.htm für den Hintergrund und Details.

Übrigens, wenn Sie auf Python 2.6 oder besser sind, dann sollten Sie stattdessen dieses metaclass eine Klasse-Dekorateur mit - wir Klasse Dekorateure genau hinzugefügt, weil so viele metaclasses wurden nur für solche Dekorationszwecke verwendet wird, und Dekorateure sind wirklich viel Verwendung einfacher.

Andere Tipps

Bearbeiten : Ich noch einmal liegen. Die __?attr__ Attribute auf Funktionen nur lesbar sind, aber anscheinend nicht immer eine AttributeException Ausnahme auslösen, wenn Sie zuweisen? Ich weiß nicht. Zurück auf Feld eins!

Bearbeiten : Dies löst nicht das Problem tatsächlich, da die Verpackungsfunktion nicht Proxy-Attribut-Anforderungen an den InstanceMethodWrapper. Ich könnte natürlich, Ente-Punsch die __?attr__ in der Dekorateur-Attribute - und es ist das, was ich jetzt tue - aber das ist hässlich. Bessere Ideen sind sehr willkommen.


Natürlich, ich war sofort klar, dass eine einfache Dekorateur mit unseren Klassen kombinieren den Trick:

def methodize(method, callable):
    "Circumvents the fact that callables are not converted to instance methods."
    @wraps(method)
    def wrapper(*args, **kw):
        return wrapper._callable(*args, **kw)
    wrapper._callable = callable
    return wrapper

Dann fügen Sie den Dekorateur auf den Aufruf in der Metaklasse InstanceMethodWrapper:

cls_dict[k] = methodize(v, InstanceMethodWrapper(v))

puh. Ein wenig schief, aber es funktioniert.

Ich vermute, Sie versuchen, eine Metaklasse zu machen, das jede Methode in der Klasse mit einer benutzerdefinierten Funktion hüllt.

Hier ist meine Version, die ich denke, ein bisschen weniger schräg ist.

import types

class CallableWrappingMeta(type):
    def __new__(mcls, name, bases, cls_dict):
        instance = type.__new__(mcls, name, bases, cls_dict)
        for k in dir(instance):
            v = getattr(instance, k)
            if isinstance(v, types.MethodType):
                setattr(instance, k, instanceMethodWrapper(v))

        return instance

def instanceMethodWrapper(function):
    def customfunc(*args, **kw):
        print "instanceMethodWrapper(*%r, **%r )" % (args, kw)
        return function(*args, **kw)
    return customfunc

class Bar(object):
    __metaclass__ = CallableWrappingMeta

    def method(self, a, b):
        print a,b

a = Bar()
a.method("foo","bar")

Ich glaube, Sie spezifischere über Ihr Problem sein müssen. Die ursprüngliche Frage spricht über eine Funktion Einwickeln, aber Ihre spätere Antwort scheint zu bewahren Funktion Attribute, die scheint zu sein, ein neuer Faktor zu sprechen. Wenn Sie Ihre Design-Ziele deutlich mehr buchstabieren, könnte es einfacher sein, Ihre Frage zu beantworten.

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