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!!
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 boby
e 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"
fonte
funcType
faz e por que é necessário.funcType = types.MethodType
(após a importaçãotypes
) em vez defuncType = type(Dog.bark)
.__get__
a função para vinculá-la à instância.Você precisa usar o MethodType do
types
módulo. O objetivoMethodType
é sobrescrever métodos de nível de instância (para queself
possam 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!!
fonte
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 viaself
.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 chamarx in instance
desconsideraria isso e usaria em seutype(instance).__contains__(instance, x)
lugar. Isso se aplica a todos os métodos mágicos especificados no modelo de dados Python .fonte
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
boby
variá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 usarself
.fonte
patching
e esta é a maneira correta de fazer (por exemplo,boby = Dog()
eboby.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.partial
aqui: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!!
fonte
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
fonte
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 é:
fonte
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
fonte
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()
fonte