Pergunta

Vamos dizer que temos uma CallableWrappingMeta metaclass que caminha o corpo de uma nova classe, envolvendo seus métodos com a 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!'

O nosso invólucro manequim apenas imprime os argumentos que eles chegam Mas você vai notar algo visível:. Método não é passado o receptor instância a objeto, porque mesmo que InstanceMethodWrapper é exigível, não é tratado como uma função para a finalidade de ser convertido em um método de instância durante a criação da classe (após a nossa metaclass é feito com ele).

Uma solução potencial é usar um decorador em vez de uma classe para embrulhar os métodos - que a função vai se tornar um método de instância. Mas no mundo real, InstanceMethodWrapper é muito mais complexa: ele fornece uma API e publica eventos método de chamada. Uma classe é mais conveniente (e de maior performance, não que isso importa muito).

Eu também tentei alguns becos sem saída. Subclassificação types.MethodType e types.UnboundMethodType não ir a qualquer lugar. Um pouco de introspecção, e parece que decend de type. Então eu tentei usar tanto como uma metaclasse, mas sem sorte lá também. Pode ser o caso que eles têm demandas especiais como uma metaclasse, mas parece que estamos em território não documentada neste momento.

Todas as idéias?

Foi útil?

Solução

Apenas enriquecê-lo InstanceMethodWrapper classe com um __get__ (que pode muito bem apenas return self) - isto é, fazer essa classe em um descritor do tipo, de modo que suas instâncias estão descritor objetos. Consulte http://users.rcn.com/python/download/Descriptor.htm para o fundo e os detalhes.

BTW, se você estiver em Python 2.6 ou melhor, considerar o uso de uma classe-decorador, em vez de que metaclass - nós adicionamos decoradores de classe exatamente porque tantas metaclasses estavam sendo usados ??apenas para tais fins de decoração e decoradores são realmente muito simples de usar.

Outras dicas

Editar : Eu mentir mais uma vez. Os atributos __?attr__ sobre as funções são somente leitura, mas aparentemente nem sempre lançar uma exceção AttributeException quando você atribui? Não sei. Voltar para um quadrado!

Editar : Isso na verdade não resolver o problema, como a função de embrulho não vai de proxy solicitações atributo à InstanceMethodWrapper. Eu poderia, é claro, pato-perfurar os atributos __?attr__ na decorador - e é o que eu estou fazendo agora - mas isso é feio. Melhores ideias são muito bem-vindos.


Claro, eu imediatamente percebi que a combinação de um decorador simples com nossas classes irá fazer o truque:

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

Em seguida, você adicionar o decorador para a chamada para InstanceMethodWrapper na metaclass:

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

Poof. Um pouco oblíqua, mas funciona.

Eu estou supondo que você está tentando fazer uma metaclasse que envolve todos os métodos na classe com uma função personalizada.

Aqui é a minha versão que eu acho que é um pouco menos oblíqua.

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

Eu acho que você precisa ser mais específico sobre o seu problema. A questão conversas originais sobre embrulhar uma função, mas a sua resposta posterior parece falar sobre a preservação de atributos de função, o que parece ser um novo fator. Se você digitou as suas metas de design de forma mais clara, pode ser mais fácil de responder a sua pergunta.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top