2009-05-02 7 views
2

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.aufrufbar als instancemethod?

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 Ich bemerke etwas Auffälliges: Die Methode wird nicht an den Instanz-Objekt-Empfänger übergeben, denn obwohl InstanceMethodWrapper aufrufbar ist, wird sie nicht als eine Funktion für die Umwandlung in eine Instanzmethode während der Klassenerstellung behandelt (nach unserer Metaklasse) damit gemacht).

Eine mögliche Lösung besteht darin, einen Decorator anstelle einer Klasse zu verwenden, um die Methoden zu umbrechen - diese Funktion wird zu einer Instanzmethode. Aber in der realen Welt ist InstanceMethodWrapper viel komplexer: Es bietet eine API und veröffentlicht Methodenaufrufereignisse. Eine Klasse ist bequemer (und leistungsfähiger, nicht dass das viel ausmacht).

Ich versuchte auch einige Sackgassen. Subclassing types.MethodType und types.UnboundMethodType gingen nirgendwohin. Eine kleine Introspektion, und es scheint, dass sie von type abweichen. Also habe ich versucht, beides als Metaklasse zu benutzen, aber auch kein Glück. Es kann sein, dass sie spezielle Anforderungen als Metaklasse haben, aber es scheint, dass wir uns an diesem Punkt in undokumentiertem Territorium befinden.

Irgendwelche Ideen?

+0

Können Sie bitte mehr erklären? Ich habe Probleme zu verstehen, was du zu tun versuchst. – Unknown

Antwort

3

nur bereichern Sie InstanceMethodWrapper Klasse mit einem __get__ (was sehr gut kann nur return self) - das heißt, dass die Klasse in einen Typ Descriptor machen, so dass ihre Instanzen Descriptor Objekte. Hintergrund und Details finden Sie unter http://users.rcn.com/python/download/Descriptor.htm. Wenn Sie auf Python 2.6 oder besser sind, sollten Sie einen Klassen-Dekorator anstelle dieser Metaklasse verwenden - wir haben Klassen-Dekoratoren genau hinzugefügt, weil so viele Metaklassen nur für solche Dekorationszwecke verwendet wurden, und Dekoratoren wirklich sind viel einfacher zu bedienen.

0

Bearbeiten: Ich liege noch einmal. Die __?attr__ Attribute auf Funktionen sind schreibgeschützt, aber anscheinend werfen Sie nicht immer eine AttributeException Ausnahme, wenn Sie zuweisen? Ich weiß nicht. Zurück auf Platz eins!

bearbeiten: Dies gilt nicht wirklich das Problem zu lösen, da die Verpackungsfunktion nicht Proxy-Attribut-Anforderungen an die InstanceMethodWrapper. Ich könnte natürlich die __?attr__ Attribute im Decorator ducken - und das mache ich jetzt - aber das ist hässlich. Bessere Ideen sind sehr willkommen.


Natürlich, ich war sofort klar, dass eine einfache Dekorateur mit unseren Klassen kombiniert 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 zu InstanceMethodWrapper im metaclass:

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

Poof. Ein bisschen schräg, aber es funktioniert.

0

Ich vermute, Sie versuchen, eine Metaklasse, die jede Methode in der Klasse mit einer benutzerdefinierten Funktion umschließt.

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

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

Ich denke, Sie müssen genauer auf Ihr Problem eingehen. Die ursprüngliche Frage bezieht sich auf das Umschließen einer Funktion, aber Ihre nachfolgende Antwort scheint über das Bewahren von Funktionsattributen zu sprechen, was ein neuer Faktor zu sein scheint. Wenn Sie Ihre Designziele klarer formulieren, könnte es einfacher sein, Ihre Frage zu beantworten.

Verwandte Themen