Как реализовать виртуальные методы в Python? - программирование
Подтвердить что ты не робот

Как реализовать виртуальные методы в Python?

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

Как они могут быть реализованы в Python?

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

4b9b3361

Ответ 1

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

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.

Ответ 2

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

Ответ 3

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?

Ответ 4

На самом деле, в версии 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

Ответ 5

Методы 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

Ответ 6

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

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

Ответ 7

Что-то вроде виртуального метода в 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(). Этот очень хороший доклад Раймонда Хеттингера дает пример для случая, когда это неудобно.