Как реализовать виртуальные методы в Python? - PullRequest
63 голосов
/ 17 января 2011

Я знаю виртуальные методы из PHP или Java.

Как их можно реализовать в Python?

Или я должен определить пустой метод в абстрактном классе и переопределить его?

Ответы [ 7 ]

89 голосов
/ 17 января 2011

Конечно, и вам даже не нужно определять метод в базовом классе. В Python методы лучше, чем виртуальные - они полностью динамические, так как в Python набирается duck, печатающий .

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 и Dog в Python даже не должны наследоваться от общего базового класса, чтобы разрешить такое поведение - вы получаете его бесплатно. Тем не менее, некоторые программисты предпочитают определять свои иерархии классов более жестко, чтобы лучше документировать их и навязывать некоторую строгость типизации. Это также возможно - см., Например, abc стандартный модуль .

46 голосов
/ 17 января 2011

Методы Python всегда виртуальны.

35 голосов

raise NotImplementedError()

Это рекомендуемое исключение для "чисто виртуальных методов" "абстрактных" базовых классов, которые не реализуют метод.

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError говорит:

Это исключение происходит от RuntimeError. В определяемых пользователем базовых классах абстрактные методы должны вызывать это исключение, когда им требуются производные классы для переопределения метода.

Как уже говорили другие, это в основном соглашение о документации и не требуется, но таким образом вы получите более значимое исключение, чем ошибка отсутствующего атрибута.

например:.

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

дает:

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

Похожие: Можно ли создавать абстрактные классы в Python?

19 голосов
/ 11 октября 2013

На самом деле, в версии 2.6 Python предоставляет нечто, называемое абстрактные базовые классы , и вы можете явно установить виртуальные методы, например:

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

Это работает очень хорошо, при условии, что класс не наследует от классов, которые уже используют метаклассы.

источник: http://docs.python.org/2/library/abc.html

8 голосов
/ 22 августа 2015

Методы Python всегда виртуальны

как сказал Игнасио Каким-то образом наследование классов может быть лучшим подходом для реализации того, что вы хотите.

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

Результаты должны быть:

I am an unknown animal
I am a dog named Rover
Rover has 4 legs
5 голосов
/ 28 июля 2017

Методы Python всегда виртуальны.

... при условии, что они не являются частными!Слишком плохо для парня из C ++.

1 голос
/ 29 декабря 2018

Что-то вроде виртуального метода в C ++ (вызов метода реализации производного класса посредством ссылки или указателя на базовый класс) не имеет смысла в Python, так как Python не имеет типирования. (Хотя я не знаю, как работают виртуальные методы в Java и PHP.)

Но если под "виртуальным" вы подразумеваете вызов самой нижней реализации в иерархии наследования, то это то, что вы всегда получаете в Python, как указывают несколько ответов.

Ну, почти всегда ...

Как указал dplamp, не все методы в Python ведут себя так. Дандер метод не делает. И я думаю, что это не очень известная особенность.

Рассмотрим этот искусственный пример

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

Теперь

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

Однако рассмотрим это

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

Теперь

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

Единственное, что мы изменили, это сделали prop_a() более грубый метод.

Проблема с первым поведением может заключаться в том, что вы не можете изменить поведение prop_a() в производном классе, не влияя на поведение prop_b(). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *. ”.

...