que pode ser chamado como instancemethod?
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?
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.