Adicionando um método a uma instância de objeto existente

643

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?

Akdom
fonte

Respostas:

921

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 .

Jason Pratt
fonte
65
Em vez de criar manualmente um 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étodo barFightersbound para o qual bound a.
Martijn Pieters
2
@MartijnPieters quaisquer vantagens de usar o descriptor protocolvs, criando um MethodTypeaparte de talvez ser um pouco mais legível.
EndermanAPM
17
@ EndermanAPM: Vários: é mais provável que continue funcionando exatamente da mesma maneira que o acesso ao atributo em uma instância. Ele vai trabalhar para classmethode staticmethode outros descritores também. Evita sobrecarregar o espaço para nome com mais uma importação.
Martijn Pieters
34
O código completo para a abordagem descritor sugerido éa.barFighters = barFighters.__get__(a)
eqzx
98

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'>
Evgeny
fonte
1
Como isso funcionaria se o método adicionado tivesse que se referir a si mesmo? Minha primeira tentativa leva a um erro de sintaxe, mas adicionar self na definição de método não parece funcionar. tipos de importação classe A (objeto): # mas parece funcionar também para objetos de estilo antigo ax = 'ax' pass def patch_me (destino): método def (destino, x): print (self.ax) print ("x =" , x) print ("chamado de", target) target.method = types.MethodType (method, target) # adicione mais se necessário a = A () print (a.ax)
WesR
85

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?

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:

  • Você adicionará um objeto vinculado a todas as instâncias em que fizer isso. Se você fizer muito isso, provavelmente perderá muita memória. Normalmente, os métodos vinculados são criados apenas para a curta duração de sua chamada e deixam de existir quando o lixo é coletado automaticamente. Se você fizer isso manualmente, terá uma ligação de nome referenciando o método bound - o que impedirá sua coleta de lixo durante o uso.
  • As instâncias de objeto de um determinado tipo geralmente têm seus métodos em todos os objetos desse tipo. Se você adicionar métodos em outro lugar, algumas instâncias terão esses métodos e outras não. Os programadores não esperam isso, e você corre o risco de violar a regra da menor surpresa .
  • Como existem outras boas razões para não fazer isso, você também terá uma má reputação se fizer isso.

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.

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.

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)

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

foo.sample_method = sample_method.__get__(foo)

e agora:

>>> foo.sample_method(1,2)
3

Método um - types.MethodType

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

Método dois: ligação lexical

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

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:

>>> 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.

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.

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

Conclusão

Você já sabe várias maneiras que você poderia fazer isso, mas com toda a seriedade - não faça isso.

Aaron Hall
fonte
1
O aviso é o que eu estava pensando. A definição de métodos é simplesmente funções aninhadas na definição de classe.
Atcold
1
@Atcold Expandi os motivos para evitar fazer isso na introdução.
Aaron Hall
O __get__método também precisa da classe como parâmetro seguinte: sample_method.__get__(foo, Foo).
Aidas Bendoraitis
2
@AidasBendoraitis Eu não diria que "precisa", é um parâmetro opcional fornecido ao aplicar o protocolo descritor - mas as funções python não usam o argumento: github.com/python/cpython/blob/master/Objects/funcobject .c # L581
Aaron Hall
Meu comentário foi baseado nesta referência: python-reference.readthedocs.io/en/latest/docs/dunderdsc/… O que agora vejo nos documentos oficiais é opcional: docs.python.org/3/howto/descriptor.html# descriptor-protocol
Aidas Bendoraitis
35

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'>
Tomasz Zieliński
fonte
20

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.

John Downey
fonte
6
Mas isso influencia todas as instâncias da classe, não apenas uma.
glglgl
14

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
Evgeny Prokurat
fonte
9

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

ndpu
fonte
7

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>
HS.
fonte
8
Isso está corrigindo a classe A, não a instância a.
Ethan Furman
5
Existe uma razão para usar em setattr(A,'printme',printme)vez de simplesmente A.printme = printme?
Tobias Kienzler 9/08/13
1
Faz sentido se alguém constrói o nome do método em tempo de execução.
RR
6

Como essa pergunta foi feita em versões não-Python, aqui está o JavaScript:

a.methodname = function () { console.log("Yay, a new method!") }
Tamzin Blake
fonte
5

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>>
Nisan.H
fonte
addmethodreescrito da seguinte maneira def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )resolve o problema
Antony Hatchkins 05/04
5

Este é 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:

# 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
Máx.
fonte
3

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 .

ChristopherC
fonte
3

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.

deitado
fonte
2

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.

Acuminate
fonte
7
Você está corrigindo a classe Test, não uma instância dela.
Ethan Furman
Você está adicionando método a uma classe, não a uma instância de objeto.
TomSawyer 29/08/19
2

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
Yu Feng
fonte
2
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

Arturo Morales Rangel
fonte