Existe uma maneira em Python de substituir um método de classe no nível da instância? Por exemplo:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Existe uma maneira em Python de substituir um método de classe no nível da instância? Por exemplo:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Respostas:
Por favor, não faça isso conforme mostrado. Seu código se torna ilegível quando você faz um monkeypatch em uma instância para ser diferente da classe.
Você não pode depurar código monkeypatched.
Quando você encontrar um bug em bobye print type(boby), verá que (a) é um cão, mas (b) por alguma razão obscura ele não late corretamente. Isso é um pesadelo. Não faça isso.
Em vez disso, faça isso.
class Dog:
def bark(self):
print "WOOF"
class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"
otherDog= Dog()
otherDog.bark() # WOOF
boby = BobyDog()
boby.bark() # WoOoOoF!!
Sim é possivel:
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
funcType = type(Dog.bark)
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)
foo.bark()
# "Woof Woof"
funcTypefaz e por que é necessário.
funcType = types.MethodType(após a importação types) em vez de funcType = type(Dog.bark).
__get__a função para vinculá-la à instância.
Você precisa usar o MethodType do typesmódulo. O objetivo MethodTypeé sobrescrever métodos de nível de instância (para que selfpossam estar disponíveis em métodos sobrescritos).
Veja o exemplo abaixo.
import types
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print "WoOoOoF!!"
boby.bark = types.MethodType(_bark, boby)
boby.bark() # WoOoOoF!!
Para explicar a excelente resposta de @codelogic, proponho uma abordagem mais explícita. Esta é a mesma técnica que o .operador usa para vincular um método de classe quando você o acessa como um atributo de instância, exceto que seu método será na verdade uma função definida fora de uma classe.
Trabalhando com o código do @codelogic, a única diferença é como o método é vinculado. Estou usando o fato de que funções e métodos não são descritores de dados em Python e invocando o __get__método. Observe particularmente que o original e a substituição têm assinaturas idênticas, o que significa que você pode escrever a substituição como um método de classe completa, acessando todos os atributos de instância via self.
classe Cachorro:
def bark (próprio):
imprimir "Woof"
def new_bark (self):
imprimir "Woof Woof"
foo = Cachorro ()
# "Woof"
foo.bark ()
# substituir bark por new_bark apenas para este objeto
foo.bark = new_bark .__ get __ (foo, Cachorro)
foo.bark ()
# "Woof Woof"
Ao atribuir o método vinculado a um atributo de instância, você criou uma simulação quase completa de substituição de um método. Um recurso útil que está faltando é o acesso à versão sem argumentos do super, já que você não está em uma definição de classe. Outra coisa é que o __name__atributo do seu método vinculado não terá o nome da função que está substituindo, como faria na definição da classe, mas você ainda pode configurá-lo manualmente. A terceira diferença é que seu método ligado manualmente é uma referência de atributo simples que por acaso é uma função. O .operador não faz nada além de buscar essa referência. Ao invocar um método regular de uma instância, por outro lado, o processo de vinculação cria um novo método vinculado a cada vez.
A única razão pela qual isso funciona, a propósito, é que os atributos da instância substituem os descritores que não são de dados . Os descritores de dados têm __set__métodos, métodos que (felizmente para você) não têm. Os descritores de dados na classe realmente têm prioridade sobre quaisquer atributos de instância. É por isso que você pode atribuir a uma propriedade: seu __set__método é invocado quando você tenta fazer uma atribuição. Pessoalmente, gosto de dar um passo adiante e ocultar o valor real do atributo subjacente na instância __dict__, onde é inacessível por meios normais exatamente porque a propriedade o obscurece.
Você também deve ter em mente que isso é inútil para métodos mágicos (sublinhado duplo) . É claro que os métodos mágicos podem ser substituídos dessa forma, mas as operações que os usam olham apenas para o tipo. Por exemplo, você pode definir __contains__algo especial em sua instância, mas chamar x in instancedesconsideraria isso e usaria em seu type(instance).__contains__(instance, x)lugar. Isso se aplica a todos os métodos mágicos especificados no modelo de dados Python .
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
def new_bark():
print "WoOoOoF!!"
boby.bark = new_bark
boby.bark() # WoOoOoF!!
Você pode usar a bobyvariável dentro da função se precisar. Desde que você está substituindo o método apenas para este objeto uma instância, desta forma é mais simples e tem exatamente o mesmo efeito que usar self.
patchinge esta é a maneira correta de fazer (por exemplo, boby = Dog()e boby.bark = new_bark). É extremamente útil em testes de unidade para controles. Para obter mais explicações, consulte tryolabs.com/blog/2013/07/05/run-time-method-patching-python (exemplos) - não, não sou afiliado ao site ou autor vinculado.
Já que ninguém está mencionando functools.partialaqui:
from functools import partial
class Dog:
name = "aaa"
def bark(self):
print("WOOF")
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print("WoOoOoF!!")
boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!
Como as funções são objetos de primeira classe em Python, você pode passá-las ao inicializar seu objeto de classe ou substituí-lo a qualquer momento para uma determinada instância de classe:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
print "woof"
d=Dog()
print "calling original bark"
d.bark()
def barknew():
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
def barknew1():
print "nowoof"
d1.bark=barknew1
print "calling another new"
d1.bark()
e os resultados são
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Embora eu tenha gostado da ideia de herança de S. Lott e concordado com o 'tipo (a)', mas como as funções também têm atributos acessíveis, acho que pode ser gerenciado desta forma:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
"""original bark"""
print "woof"
d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__
def barknew():
"""a new type of bark"""
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
def barknew1():
"""another type of new bark"""
print "nowoof"
d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
e a saída é:
calling original bark
woof
that was original bark
calling the new bark
wooOOOoof
that was a new type of bark
another new
nowoof
that was another type of new bark
Caro, isso não é anulação, você está apenas chamando a mesma função duas vezes com o objeto. Basicamente, a substituição está relacionada a mais de uma classe. quando o mesmo método de assinatura existe em classes diferentes, qual função você está chamando, decide o objeto que a chama. A substituição é possível em python quando você faz com que mais de uma classe escreva as mesmas funções e mais uma coisa para compartilhar que a sobrecarga não é permitida em python
Achei que esta é a resposta mais precisa para a pergunta original
https://stackoverflow.com/a/10829381/7640677
import a
def _new_print_message(message):
print "NEW:", message
a.print_message = _new_print_message
import b
b.execute()