Domanda

Diciamo che abbiamo una metaclasse CallableWrappingMeta che accompagna il corpo di una nuova classe, avvolgendo i suoi metodi con una 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!'

Il nostro finto wrapper stampa gli argomenti appena arrivano. Ma noterai qualcosa di evidente: il metodo non viene passato al ricevitore dell'istanza-oggetto, perché anche se InstanceMethodWrapper è richiamabile, esso non viene trattato come una funzione allo scopo di essere convertito in un metodo di istanza durante la creazione della classe (dopo che la nostra metaclasse è stata eseguita con essa).

Una potenziale soluzione è usare un decoratore invece di una classe per avvolgere i metodi - quella funzione diventerà un metodo di istanza. Ma nel mondo reale, InstanceMethodWrapper è molto più complesso: fornisce un'API e pubblica eventi di chiamata di metodo. Una lezione è più conveniente (e più performante, non che questo contenga molto).

Ho anche provato alcuni vicoli ciechi. La sottoclasse dei tipi .MethodType e .UnboundMethodType non è andata da nessuna parte. Un po 'di introspezione, e sembra che declinino dal type . Quindi ho provato a usarli entrambi come metaclasse, ma non ho avuto fortuna neanche lì. Potrebbe essere il caso in cui abbiano richieste particolari come metaclasse, ma a questo punto sembra che siamo in un territorio non documentato.

Qualche idea?

È stato utile?

Soluzione

Arricchisci la tua classe InstanceMethodWrapper con un __get__ (che può benissimo solo restituire self ) - cioè trasformare quella classe in un tipo descrittore , in modo che le sue istanze siano oggetti descrittori. Vedi http://users.rcn.com/python/download/Descriptor.htm per sfondo e dettagli.

A proposito, se sei su Python 2.6 o superiore, considera l'utilizzo di un decoratore di classe invece di quella metaclasse - abbiamo aggiunto decoratori di classe esattamente perché così tanti metaclassi venivano usati solo per tali scopi di decorazione e i decoratori sono davvero molto più semplice da usare.

Altri suggerimenti

Modifica : mento ancora una volta. Gli attributi __? Attr__ sulle funzioni sono di sola lettura, ma apparentemente non generano sempre un'eccezione AttributeException quando assegni? Non so. Torna al punto di partenza!

Modifica : questo in realtà non risolve il problema, poiché la funzione di wrapping non inoltra richieste di attributo a InstanceMethodWrapper . Potrei, ovviamente, dare un pugno agli attributi __? Attr__ nel decoratore - ed è quello che sto facendo ora - ma è brutto. Le idee migliori sono le benvenute.


Ovviamente, ho immediatamente capito che combinare un semplice decoratore con le nostre lezioni farà il trucco:

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

Quindi aggiungi il decoratore alla chiamata a InstanceMethodWrapper nella metaclasse:

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

Puff. Un po 'obliquo, ma funziona.

Suppongo che tu stia provando a creare una metaclasse che avvolga tutti i metodi della classe con una funzione personalizzata.

Ecco la mia versione che penso sia un po 'meno obliqua.

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

Penso che tu debba essere più specifico sul tuo problema. La domanda originale parla del wrapping di una funzione, ma la tua risposta successiva sembra parlare di preservare gli attributi della funzione, che sembra essere un nuovo fattore. Se hai spiegato più chiaramente i tuoi obiettivi di progettazione, potrebbe essere più semplice rispondere alla tua domanda.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top