Pregunta

Digamos que tenemos una metaclase CallableWrappingMeta que recorre el cuerpo de una nueva clase, envolviendo sus métodos con una clase, 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!'

Nuestro contenedor ficticio solo imprime los argumentos a medida que entran. Pero notará algo llamativo: el método no pasa el receptor de instancia-objeto, porque a pesar de que InstanceMethodWrapper es invocable, no se trata como una función con el propósito de convertirse en un método de instancia durante la creación de la clase (después de que nuestra metaclase haya terminado con ella).

Una solución potencial es usar un decorador en lugar de una clase para envolver los métodos, esa función se convertirá en un método de instancia. Pero en el mundo real, InstanceMethodWrapper es mucho más complejo: proporciona una API y publica eventos de llamadas a métodos. Una clase es más conveniente (y más eficaz, no que esto importe mucho).

También probé algunos callejones sin salida. La subclasificación de types.MethodType y types.UnboundMethodType no fue a ninguna parte. Un poco de introspección, y parece que descienden de type . Así que intenté usar ambos como metaclase, pero tampoco tuve suerte. Puede ser que tengan demandas especiales como metaclase, pero parece que estamos en territorio indocumentado en este punto.

¿Alguna idea?

¿Fue útil?

Solución

Solo te enriquecemos con la clase InstanceMethodWrapper con un __get__ (que puede perfectamente bien solo return self ), es decir, convierte esa clase en un tipo descriptor , de modo que sus instancias sean objetos descriptores. Consulte http://users.rcn.com/python/download/Descriptor.htm para antecedentes y detalles.

Por cierto, si estás en Python 2.6 o mejor, considera usar un decorador de clase en lugar de esa metaclase. Agregamos decoradores de clase exactamente porque se utilizaron muchas metaclases solo para dichos fines de decoración, y los decoradores son realmente muy buenos. más simple de usar.

Otros consejos

Editar : miento otra vez. Los atributos __? Attr__ en las funciones son de solo lectura, pero aparentemente no siempre arrojan una excepción AttributeException cuando asigna? No se. ¡De vuelta al punto de partida!

Editar : En realidad, esto no resuelve el problema, ya que la función de ajuste no enviará solicitudes de atributos al InstanceMethodWrapper . Podría, por supuesto, golpear los atributos __? Attr__ en el decorador, y es lo que estoy haciendo ahora, pero eso es feo. Las mejores ideas son bienvenidas.


Por supuesto, inmediatamente me di cuenta de que combinar un decorador simple con nuestras clases hará el truco:

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

Luego agrega el decorador a la llamada a InstanceMethodWrapper en la metaclase:

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

Poof. Un poco oblicuo, pero funciona.

Supongo que estás tratando de hacer una metaclase que envuelva todos los métodos de la clase con una función personalizada.

Aquí está mi versión, que creo que es un poco menos oblicua.

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

Creo que debes ser más específico sobre tu problema. La pregunta original habla sobre ajustar una función, pero su respuesta posterior parece hablar sobre preservar los atributos de la función, lo que parece ser un factor nuevo. Si explica con mayor claridad sus objetivos de diseño, podría ser más fácil responder a su pregunta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top