Respostas:
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 .
descriptor protocolvs, criando um MethodTypeaparte de talvez ser um pouco mais legível.
classmethode staticmethode 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.
import types
class A(object):#but seems to work for old style objects too
pass
def patch_me(target):
def method(target,x):
print "x=",x
print "called from", target
target.method = types.MethodType(method,target)
#add more if needed
a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>
patch_me(a) #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6) #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
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 .
Adicionando um método a uma instância de objeto existente
Eu li que é possível adicionar um método a um objeto existente (por exemplo, não na definição de classe) em Python.
Entendo que nem sempre é uma boa decisão fazê-lo. Mas, como alguém pode fazer isso?
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:
Foo.sample_method = sample_method
Como é instrutivo, no entanto, vou mostrar algumas maneiras de fazer isso.
Aqui está um código de instalação. Precisamos de uma definição de classe. Pode ser importado, mas realmente não importa.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Crie uma instância:
foo = Foo()
Crie um método para adicionar a ele:
def sample_method(self, bar, baz):
print(bar + baz)
__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".
foo.sample_method = sample_method.__get__(foo)
e agora:
>>> foo.sample_method(1,2)
3
Primeiro, tipos de importação, dos quais obteremos o construtor do método:
import types
Agora adicionamos o método à instância. Para fazer isso, exigimos o construtor MethodType do typesmódulo (que importamos acima).
A assinatura do argumento para types.MethodType é (function, instance, class):
foo.sample_method = types.MethodType(sample_method, foo, Foo)
e uso:
>>> foo.sample_method(1,2)
3
Primeiro, criamos uma função de wrapper que vincula o método à instância:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
uso:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
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:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Isso faz sentido quando você considera que métodos vinculados são funções parciais da instância.
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.
>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)
Podemos fazer com que a função não acoplada funcione passando explicitamente a instância (ou qualquer selfoutra 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):
>>> foo.sample_method(foo, 1, 2)
3
Você já sabe várias maneiras que você poderia fazer isso, mas com toda a seriedade - não faça isso.
__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:
class A(object):
def m(self):
pass
Agora, vamos brincar com ele no ipython:
In [2]: A.m
Out[2]: <unbound method A.m>
Ok, então m () de alguma forma torna-se um método não ligada de um . Mas é realmente assim?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
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'):
In [11]: class MetaA(type):
....: def __getattribute__(self, attr_name):
....: print str(self), '-', attr_name
In [12]: class A(object):
....: __metaclass__ = MetaA
In [23]: A.m
<class '__main__.A'> - m
<class '__main__.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:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
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:
B.m = m
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:
b.m = types.MethodType(m, b)
A propósito:
In [2]: A.m
Out[2]: <unbound method A.m>
In [59]: type(A.m)
Out[59]: <type 'instancemethod'>
In [60]: type(b.m)
Out[60]: <type 'instancemethod'>
In [61]: types.MethodType
Out[61]: <type 'instancemethod'>
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 :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
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.
Você pode usar o lambda para ligar um método a uma instância:
def run(self):
print self._instanceString
class A(object):
def __init__(self):
self._instanceString = "This is instance string"
a = A()
a.run = lambda: run(a)
a.run()
Resultado:
This is instance string
Há pelo menos duas maneiras de anexar um método a uma instância sem types.MethodType:
>>> class A:
... def m(self):
... print 'im m, invoked with: ', self
>>> a = A()
>>> a.m()
im m, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>>
>>> def foo(firstargument):
... print 'im foo, invoked with: ', firstargument
>>> foo
<function foo at 0x978548c>
1:
>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>
2:
>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>
Links úteis:
Modelo de dados - chamando descritores
Guia do HowTo do Descritor - chamando descritores
O que você está procurando é setattrque acredito. Use isso para definir um atributo em um objeto.
>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
A, não a instância a.
setattr(A,'printme',printme)vez de simplesmente A.printme = printme?
Como essa pergunta foi feita em versões não-Python, aqui está o JavaScript:
a.methodname = function () { console.log("Yay, a new method!") }
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.
#!/usr/bin/python -u
import types
import inspect
## dynamically adding methods to a unique instance of a class
# get a list of a class's method type attributes
def listattr(c):
for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
print m[0], m[1]
# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
c.__dict__[name] = types.MethodType(method, c)
class C():
r = 10 # class attribute variable to test bound scope
def __init__(self):
pass
#internally bind a function as a method of self's class -- note that this one has issues!
def addmethod(self, method, name):
self.__dict__[name] = types.MethodType( method, self.__class__ )
# predfined function to compare with
def f0(self, x):
print 'f0\tx = %d\tr = %d' % ( x, self.r)
a = C() # created before modified instnace
b = C() # modified instnace
def f1(self, x): # bind internally
print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
print 'f4\tx = %d\tr = %d' % ( x, self.r )
b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')
b.f0(0) # OUT: f0 x = 0 r = 10
b.f1(1) # OUT: f1 x = 1 r = 10
b.f2(2) # OUT: f2 x = 2 r = 10
b.f3(3) # OUT: f3 x = 3 r = 10
b.f4(4) # OUT: f4 x = 4 r = 10
k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)
b.f0(0) # OUT: f0 x = 0 r = 2
b.f1(1) # OUT: f1 x = 1 r = 10 !!!!!!!!!
b.f2(2) # OUT: f2 x = 2 r = 2
b.f3(3) # OUT: f3 x = 3 r = 2
b.f4(4) # OUT: f4 x = 4 r = 2
c = C() # created after modifying instance
# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>
print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>
print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>
Pessoalmente, prefiro a rota de função externa ADDMETHOD, pois ela também permite atribuir dinamicamente novos nomes de métodos em um iterador.
def y(self, x):
pass
d = C()
for i in range(1,5):
ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
addmethodreescrito da seguinte maneira def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )resolve o problema
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:
# this class resides inside ReloadCodeDemo.py
class A:
def bar( self ):
print "bar1"
def reloadCode(self, methodName):
''' use this function to reload any function of class A'''
import types
import ReloadCodeDemo as ReloadMod # import the code as module
reload (ReloadMod) # force a reload of the module
myM = getattr(ReloadMod.A,methodName) #get reloaded Method
myTempFunc = types.FunctionType(# convert the method to a simple function
myM.im_func.func_code, #the methods code
globals(), # globals to use
argdefs=myM.im_func.func_defaults # default values for variables if any
)
myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
setattr(self,methodName,myNewM) # add the method to the function
if __name__ == '__main__':
a = A()
a.bar()
# now change your code and save the file
a.reloadCode('bar') # reloads the file
a.bar() # now executes the reloaded code
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 nomeado guineapigé o seguinte:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
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 .
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:
def binder (function, instance):
copy_of_function = type (function) (function.func_code, {})
copy_of_function.__bind_to__ = instance
def bound_function (*args, **kwargs):
return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
return bound_function
class SupaClass (object):
def __init__ (self):
self.supaAttribute = 42
def new_method (self):
print self.supaAttribute
supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)
otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)
otherInstance.supMethod ()
supaInstance.supMethod ()
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.
O que Jason Pratt postou está correto.
>>> class Test(object):
... def a(self):
... pass
...
>>> def b(self):
... pass
...
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>
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.
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:
def addmethod(obj, name, func):
klass = obj.__class__
subclass = type(klass.__name__, (klass,), {})
setattr(subclass, name, func)
obj.__class__ = subclass
from types import MethodType
def method(self):
print 'hi!'
setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
Com isso, você pode usar o auto-ponteiro
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étodobarFightersbound para o qual bounda.