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