Eu li que é possível adicionar um método a um objeto existente (ou seja, não na definição de classe) em Python.
Entendo que nem sempre é bom fazê-lo. Mas como alguém pode fazer isso?
fonte
Eu li que é possível adicionar um método a um objeto existente (ou seja, não na definição de classe) em Python.
Entendo que nem sempre é bom fazê-lo. Mas como alguém pode fazer isso?
No Python, há uma diferença entre funções e métodos vinculados.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Os métodos vinculados foram "vinculados" (quão descritivos) a uma instância, e essa instância será passada como o primeiro argumento sempre que o método for chamado.
Callables que são atributos de uma classe (em oposição a uma instância) ainda são ilimitados, portanto, você pode modificar a definição de classe sempre que quiser:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Instâncias definidas anteriormente também são atualizadas (desde que não tenham substituído o atributo):
>>> a.fooFighters()
fooFighters
O problema ocorre quando você deseja anexar um método a uma única instância:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
A função não é vinculada automaticamente quando é anexada diretamente a uma instância:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Para vinculá-lo, podemos usar a função MethodType no módulo de tipos :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Desta vez, outras instâncias da classe não foram afetadas:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Mais informações podem ser encontradas lendo sobre descritores e programação de metaclasse .
MethodType
, chame o protocolo do descritor manualmente e faça com que a função produza sua instância:barFighters.__get__(a)
produz um métodobarFighters
bound para o qual bounda
.descriptor protocol
vs, criando umMethodType
aparte de talvez ser um pouco mais legível.classmethod
estaticmethod
e outros descritores também. Evita sobrecarregar o espaço para nome com mais uma importação.a.barFighters = barFighters.__get__(a)
O módulo new está obsoleto desde o python 2.6 e removido no 3.0, use tipos
consulte http://docs.python.org/library/new.html
No exemplo abaixo, removi deliberadamente o valor de retorno da
patch_me()
função. Eu acho que dar valor de retorno pode fazer alguém acreditar que o patch retorna um novo objeto, o que não é verdade - ele modifica o de entrada. Provavelmente, isso pode facilitar um uso mais disciplinado do monkeypatching.fonte
Prefácio - uma observação sobre compatibilidade: outras respostas podem funcionar apenas no Python 2 - essa resposta deve funcionar perfeitamente bem no Python 2 e 3. Se você escrever apenas o Python 3, poderá deixar de herdar explicitamente o conteúdo
object
, mas, caso contrário, o código permanecerá o mesmo .Sim, é possível - mas não recomendado
Eu não recomendo isso. Esta é uma má ideia. Não faça isso.
Aqui estão algumas razões:
Portanto, sugiro que você não faça isso, a menos que tenha um bom motivo. É muito melhor para definir o método correcto na definição de classe ou menos preferencialmente para-macaco remendo da classe directamente, como este:
Como é instrutivo, no entanto, vou mostrar algumas maneiras de fazer isso.
Como pode ser feito
Aqui está um código de instalação. Precisamos de uma definição de classe. Pode ser importado, mas realmente não importa.
Crie uma instância:
Crie um método para adicionar a ele:
Método nada (0) - use o método descritor,
__get__
Pesquisas pontilhadas em funções chamam o
__get__
método da função com a instância, vinculando o objeto ao método e, assim, criando um "método vinculado".e agora:
Método um - types.MethodType
Primeiro, tipos de importação, dos quais obteremos o construtor do método:
Agora adicionamos o método à instância. Para fazer isso, exigimos o construtor MethodType do
types
módulo (que importamos acima).A assinatura do argumento para types.MethodType é
(function, instance, class)
:e uso:
Método dois: ligação lexical
Primeiro, criamos uma função de wrapper que vincula o método à instância:
uso:
Método três: functools.partial
Uma função parcial aplica o (s) primeiro (s) argumento (s) a uma função (e opcionalmente argumentos de palavra-chave) e pode ser chamada posteriormente com os argumentos restantes (e substituindo argumentos de palavra-chave). Portanto:
Isso faz sentido quando você considera que métodos vinculados são funções parciais da instância.
Função não vinculada como um atributo de objeto - por que isso não funciona:
Se tentarmos adicionar o sample_method da mesma maneira que poderíamos adicioná-lo à classe, ele será desvinculado da instância e não considerará o self implícito como o primeiro argumento.
Podemos fazer com que a função não acoplada funcione passando explicitamente a instância (ou qualquer
self
outra coisa, já que esse método realmente não usa a variável de argumento), mas não seria consistente com a assinatura esperada de outras instâncias (se estivermos usando patch de macaco nesta instância):Conclusão
Você já sabe várias maneiras que você poderia fazer isso, mas com toda a seriedade - não faça isso.
fonte
__get__
método também precisa da classe como parâmetro seguinte:sample_method.__get__(foo, Foo)
.Eu acho que as respostas acima perderam o ponto principal.
Vamos ter uma classe com um método:
Agora, vamos brincar com ele no ipython:
Ok, então m () de alguma forma torna-se um método não ligada de um . Mas é realmente assim?
Acontece que m () é apenas uma função, referência à qual é adicionada ao dicionário de classe A - não há mágica. Então, por que Am nos dá um método ilimitado? É porque o ponto não é traduzido para uma simples pesquisa de dicionário. É de fato uma chamada da classe A .__ __.__ getattribute __ (A, 'm'):
Agora, não sei direito por que a última linha é impressa duas vezes, mas ainda está claro o que está acontecendo lá.
Agora, o que o padrão __getattribute__ faz é verificar se o atributo é o chamado descritor ou não, ou seja, se ele implementa um método __get__ especial. Se implementar esse método, o que é retornado é o resultado da chamada desse método __get__. Voltando à primeira versão da nossa classe A , é isso que temos:
E, como as funções Python implementam o protocolo descritor, se forem chamadas em nome de um objeto, elas se ligam a esse objeto no método __get__.
Ok, então como adicionar um método a um objeto existente? Supondo que você não se importe de corrigir as classes, é tão simples quanto:
Então Bm "se torna" um método não vinculado, graças à mágica do descritor.
E se você deseja adicionar um método apenas a um único objeto, é necessário emular o mecanismo usando os tipos.MethodType:
A propósito:
fonte
No Python, o patch do macaco geralmente funciona substituindo uma assinatura de classe ou função pela sua. Abaixo está um exemplo do Wiki do Zope :
Esse código substituirá / criará um método chamado speak na classe. No post recente de Jeff Atwood sobre remendo de macacos . Ele mostra um exemplo no C # 3.0, que é o idioma atual que eu uso no trabalho.
fonte
Você pode usar o lambda para ligar um método a uma instância:
Resultado:
fonte
Há pelo menos duas maneiras de anexar um método a uma instância sem
types.MethodType
:1:
2:
Links úteis:
Modelo de dados - chamando descritores
Guia do HowTo do Descritor - chamando descritores
fonte
O que você está procurando é
setattr
que acredito. Use isso para definir um atributo em um objeto.fonte
A
, não a instânciaa
.setattr(A,'printme',printme)
vez de simplesmenteA.printme = printme
?Como essa pergunta foi feita em versões não-Python, aqui está o JavaScript:
fonte
Consolidando as respostas do wiki de Jason Pratt e da comunidade, com uma olhada nos resultados de diferentes métodos de encadernação:
Observe especialmente como a adição da função de ligação como método de classe funciona , mas o escopo de referência está incorreto.
Pessoalmente, prefiro a rota de função externa ADDMETHOD, pois ela também permite atribuir dinamicamente novos nomes de métodos em um iterador.
fonte
addmethod
reescrito da seguinte maneiradef addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
resolve o problemaEste é realmente um complemento para a resposta de "Jason Pratt"
Embora a resposta do Jasons funcione, ela só funciona se alguém quiser adicionar uma função a uma classe. Não funcionou para mim quando tentei recarregar um método já existente do arquivo de código-fonte .py.
Demorei muito tempo para encontrar uma solução alternativa, mas o truque parece simples ... 1.st importe o código do arquivo de código-fonte 2.e force uma recarga 3.rd use types.FunctionType (...) para converter o método importado e vinculado a uma função, você também pode passar as variáveis globais atuais, pois o método recarregado estaria em um espaço de nome diferente 4.th agora você pode continuar conforme sugerido por "Jason Pratt" usando os tipos.MethodType (... )
Exemplo:
fonte
Se puder ser de alguma ajuda, liberei recentemente uma biblioteca Python chamada Gorilla para tornar o processo de patch de macaco mais conveniente.
O uso de uma função
needle()
para corrigir um módulo nomeadoguineapig
é o seguinte:Mas ele também cuida de casos de uso mais interessantes, como mostra o FAQ da documentação .
O código está disponível no GitHub .
fonte
Esta questão foi aberta anos atrás, mas, ei, existe uma maneira fácil de simular a ligação de uma função a uma instância de classe usando decoradores:
Lá, quando você passa a função e a instância para o decorador do fichário, ele cria uma nova função, com o mesmo objeto de código que o primeiro. Em seguida, a instância especificada da classe é armazenada em um atributo da função recém-criada. O decorador retorna uma (terceira) função que chama automaticamente a função copiada, fornecendo a instância como o primeiro parâmetro.
Em conclusão, você obtém uma função simulando sua ligação à instância da classe. Deixando a função original inalterada.
fonte
O que Jason Pratt postou está correto.
Como você pode ver, o Python não considera b () diferente de a (). No Python, todos os métodos são apenas variáveis que são funções.
fonte
Test
, não uma instância dela.Acho estranho que ninguém tenha mencionado que todos os métodos listados acima criam uma referência de ciclo entre o método adicionado e a instância, fazendo com que o objeto seja persistente até a coleta de lixo. Havia um velho truque ao adicionar um descritor estendendo a classe do objeto:
fonte
Com isso, você pode usar o auto-ponteiro
fonte