Eu conheço métodos virtuais de PHP ou Java.
Como eles podem ser implementados em Python?
Ou devo definir um método vazio em uma classe abstrata e substituí-lo?
Respostas:
Claro, e você nem precisa definir um método na classe base. Em Python, os métodos são melhores do que virtuais - eles são completamente dinâmicos, pois a digitação em Python é uma digitação de pato .
class Dog:
def say(self):
print "hau"
class Cat:
def say(self):
print "meow"
pet = Dog()
pet.say() # prints "hau"
another_pet = Cat()
another_pet.say() # prints "meow"
my_pets = [pet, another_pet]
for a_pet in my_pets:
a_pet.say()
Cat
e Dog
em Python nem mesmo precisa derivar de uma classe base comum para permitir esse comportamento - você o obtém de graça. Dito isso, alguns programadores preferem definir suas hierarquias de classes de uma maneira mais rígida para documentá-las melhor e impor alguma rigidez de digitação. Isso também é possível - consulte, por exemplo, o abc
módulo padrão .
raise NotImplementedError()
Esta é a exceção recomendada para gerar "métodos virtuais puros" de classes base "abstratas" que não implementam um método.
https://docs.python.org/3.5/library/exceptions.html#NotImplementedError diz:
Esta exceção é derivada de
RuntimeError
. Em classes base definidas pelo usuário, os métodos abstratos devem gerar essa exceção quando requerem classes derivadas para substituir o método.
Como outros disseram, esta é principalmente uma convenção de documentação e não é necessária, mas desta forma você obtém uma exceção mais significativa do que um erro de atributo ausente.
Por exemplo:
class Base(object):
def virtualMethod(self):
raise NotImplementedError()
def usesVirtualMethod(self):
return self.virtualMethod() + 1
class Derived(Base):
def virtualMethod(self):
return 1
print Derived().usesVirtualMethod()
Base().usesVirtualMethod()
dá:
2
Traceback (most recent call last):
File "./a.py", line 13, in <module>
Base().usesVirtualMethod()
File "./a.py", line 6, in usesVirtualMethod
return self.virtualMethod() + 1
File "./a.py", line 4, in virtualMethod
raise NotImplementedError()
NotImplementedError
Relacionado: É possível fazer classes abstratas em Python?
Os métodos Python são sempre virtuais.
Na verdade, na versão 2.6, o python fornece algo chamado classes básicas abstratas e você pode definir métodos virtuais explicitamente como este:
from abc import ABCMeta
from abc import abstractmethod
...
class C:
__metaclass__ = ABCMeta
@abstractmethod
def my_abstract_method(self, ...):
Funciona muito bem, desde que a classe não herde de classes que já usam metaclasses.
Os métodos Python são sempre virtuais
como Ignacio disse ainda De alguma forma, a herança de classes pode ser uma abordagem melhor para implementar o que você deseja.
class Animal:
def __init__(self,name,legs):
self.name = name
self.legs = legs
def getLegs(self):
return "{0} has {1} legs".format(self.name, self.legs)
def says(self):
return "I am an unknown animal"
class Dog(Animal): # <Dog inherits from Animal here (all methods as well)
def says(self): # <Called instead of Animal says method
return "I am a dog named {0}".format(self.name)
def somethingOnlyADogCanDo(self):
return "be loyal"
formless = Animal("Animal", 0)
rover = Dog("Rover", 4) #<calls initialization method from animal
print(formless.says()) # <calls animal say method
print(rover.says()) #<calls Dog says method
print(rover.getLegs()) #<calls getLegs method from animal class
Os resultados devem ser:
I am an unknown animal
I am a dog named Rover
Rover has 4 legs
Algo como um método virtual em C ++ (chamar a implementação de método de uma classe derivada por meio de uma referência ou ponteiro para a classe base) não faz sentido em Python, pois Python não tem digitação. (Não sei como os métodos virtuais funcionam em Java e PHP.)
Mas se por "virtual" você quer dizer chamar a implementação mais inferior na hierarquia de herança, então é isso que você sempre obtém em Python, como várias respostas apontam.
Bem, quase sempre ...
Como dplamp apontou, nem todos os métodos em Python se comportam assim. O método Dunder não. E acho que esse é um recurso não muito conhecido.
Considere este exemplo artificial
class A:
def prop_a(self):
return 1
def prop_b(self):
return 10 * self.prop_a()
class B(A):
def prop_a(self):
return 2
Agora
>>> B().prop_b()
20
>>> A().prob_b()
10
No entanto, considere este
class A:
def __prop_a(self):
return 1
def prop_b(self):
return 10 * self.__prop_a()
class B(A):
def __prop_a(self):
return 2
Agora
>>> B().prop_b()
10
>>> A().prob_b()
10
A única coisa que prop_a()
mudamos foi fazer um método dunder.
Um problema com o primeiro comportamento pode ser que você não pode alterar o comportamento de prop_a()
na classe derivada sem afetar o comportamento de prop_b()
. Esta palestra muito agradável de Raymond Hettinger dá um exemplo de um caso de uso em que isso é inconveniente.