web-dev-qa-db-fra.com

Comment implémenter des méthodes virtuelles en Python?

Je connais les méthodes virtuelles de PHP ou Java.

Comment peuvent-ils être implémentés en Python?

Ou dois-je définir une méthode vide dans une classe abstraite et la remplacer?

67
Meloun

Bien sûr, et vous n'avez même pas besoin de définir une méthode dans la classe de base. Dans Python sont meilleures que virtuelles - elles sont complètement dynamiques, car la saisie dans Python est la saisie de canard .

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 et Dog in Python n'a même pas à dériver d'une classe de base commune pour autoriser ce comportement - vous l'obtenez gratuitement. Cela dit, certains programmeurs préfèrent définir leurs hiérarchies de classes de manière plus rigide pour mieux les documenter et imposer une certaine rigueur de frappe. Ceci est également possible - voir par exemple le module standard abc .

92
Eli Bendersky

Les méthodes Python sont toujours virtuelles.

49

raise NotImplementedError()

Il s'agit de l'exception recommandée pour lever sur les "méthodes virtuelles pures" des classes de base "abstraites" qui n'implémentent pas de méthode.

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError dit:

Cette exception est dérivée de RuntimeError. Dans les classes de base définies par l'utilisateur, les méthodes abstraites doivent déclencher cette exception lorsqu'elles nécessitent que les classes dérivées remplacent la méthode.

Comme d'autres l'ont dit, il s'agit principalement d'une convention de documentation et n'est pas obligatoire, mais de cette façon, vous obtenez une exception plus significative qu'une erreur d'attribut manquante.

Par exemple.:

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

donne:

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

Connexes: Est-il possible de créer des classes abstraites en Python?

En fait, dans la version 2.6 python fournit quelque chose appelé classes de base abstraites et vous pouvez définir explicitement des méthodes virtuelles comme ceci:

from abc import ABCMeta
from abc import abstractmethod
...
class C:
    __metaclass__ = ABCMeta
    @abstractmethod
    def my_abstract_method(self, ...):

Cela fonctionne très bien, à condition que la classe n'hérite pas des classes qui utilisent déjà des métaclasses.

source: http://docs.python.org/2/library/abc.html

19
user2795020

Les méthodes Python sont toujours virtuelles

comme l'a dit Ignacio, l'héritage de classe peut être une meilleure approche pour implémenter ce que vous voulez.

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

Les résultats doivent être:

I am an unknown animal
I am a dog named Rover
Rover has 4 legs
8
Jinzo

Les méthodes Python sont toujours virtuelles.

... à condition qu'ils ne soient pas privés! Dommage pour un gars C++.

5
dplamp

Quelque chose comme une méthode virtuelle en C++ (appel à l'implémentation d'une méthode d'une classe dérivée via une référence ou un pointeur vers la classe de base) n'a pas de sens en Python, car Python n'a pas de typage. (Je ne sais pas comment fonctionnent les méthodes virtuelles dans Java et PHP cependant.)

Mais si par "virtuel" vous entendez appeler l'implémentation la plus basse de la hiérarchie d'héritage, c'est ce que vous obtenez toujours en Python, comme le soulignent plusieurs réponses.

Enfin, presque toujours ...

Comme l'a souligné dplamp, toutes les méthodes de Python ne se comportent pas comme ça. La méthode Dunder ne le fait pas. Et je pense que c'est une fonctionnalité pas si connue.

Considérez cet exemple artificiel

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

À présent

>>> B().prop_b()
20
>>> A().prob_b()
10

Cependant, considérez celui-ci

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

À présent

>>> B().prop_b()
10
>>> A().prob_b()
10

La seule chose que nous avons modifiée était de faire de prop_a() une méthode dunder.

Un problème avec le premier comportement peut être que vous ne pouvez pas modifier le comportement de prop_a() dans la classe dérivée sans affecter le comportement de prop_b(). This très beau discours de Raymond Hettinger donne un exemple de cas d'utilisation où cela n'est pas pratique.

2
Konstantin