Question

Supposons que nous ayons une métaclasse CallableWrappingMeta qui parcourt le corps d'une nouvelle classe, encapsulant ses méthodes avec une classe, 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!'

Notre enveloppe factice imprime simplement les arguments au fur et à mesure qu'ils arrivent. Mais vous remarquerez quelque chose de remarquable: la méthode n'est pas transmise au destinataire d'instance-objet, car même si InstanceMethodWrapper est appelable, il n’est pas traité comme une fonction dans le but d’être converti en une méthode d’instance lors de la création de la classe (après que notre métaclasse soit terminée avec elle).

Une solution potentielle consiste à utiliser un décorateur au lieu d'une classe pour envelopper les méthodes - cette fonction deviendra une méthode d'instance. Mais dans le monde réel, InstanceMethodWrapper est beaucoup plus complexe: il fournit une API et publie des événements d'appel de méthode. Une classe est plus pratique (et plus performante, cela ne compte pas beaucoup).

J'ai aussi essayé des impasses. Les sous-classes types.MethodType et types.UnboundMethodType ne sont allés nulle part. Un peu d’introspection, et il semble qu’ils soient issus de type . J'ai donc essayé d'utiliser les deux comme métaclasse, mais sans succès non plus. Il se peut qu’ils aient des exigences particulières en tant que métaclasse, mais il semble que nous soyons en territoire non documenté à ce stade.

Des idées?

Était-ce utile?

La solution

Enrichissez votre classe InstanceMethodWrapper avec un __ get __ (qui peut parfaitement bien se retourner ) - autrement dit, transformez cette classe en un type descripteur , afin que ses instances soient des objets descripteurs. Voir http://users.rcn.com/python/download/Descriptor.htm pour le fond et les détails.

BTW, si vous utilisez Python 2.6 ou une version supérieure, envisagez d'utiliser un décorateur de classe au lieu de cette métaclasse - nous avons ajouté des décorateurs de classe exactement parce qu'un grand nombre de métaclasses étaient utilisées uniquement à cette fin, et les décorateurs sont vraiment beaucoup plus simple à utiliser.

Autres conseils

Modifier : je mens encore une fois. Les attributs __? Attr __ des fonctions sont en lecture seule, mais apparemment, ils ne génèrent pas toujours une exception AttributeException lorsque vous affectez? Je ne sais pas. Retour à la case départ!

Modifier : cela ne résout pas le problème car la fonction d'habillage ne refusera pas les demandes d'attributs proxy à InstanceMethodWrapper . Je pourrais bien sûr écraser les attributs __? Attr __ dans le décorateur - et c'est ce que je fais maintenant - mais c'est moche. Les meilleures idées sont les bienvenues.

Bien sûr, j'ai tout de suite compris que combiner un décorateur simple avec nos classes ferait l'affaire:

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

Vous ajoutez ensuite le décorateur à l'appel de InstanceMethodWrapper dans la métaclasse:

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

Poof. Un peu oblique, mais ça marche.

Je suppose que vous essayez de créer une métaclasse qui enveloppe chaque méthode de la classe avec une fonction personnalisée.

Voici ma version qui, à mon avis, est un peu moins oblique.

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

Je pense que vous devez être plus précis sur votre problème. La question initiale parle d'encapsuler une fonction, mais votre réponse suivante semble parler de la préservation des attributs de la fonction, ce qui semble être un nouveau facteur. Si vous définissez plus clairement vos objectifs de conception, il sera peut-être plus facile de répondre à votre question.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top