Я знаю виртуальные методы из PHP или Java.
Как они могут быть реализованы в Python?
Или мне нужно определить пустой метод в абстрактном классе и переопределить его?
Я знаю виртуальные методы из PHP или Java.
Как они могут быть реализованы в Python?
Или мне нужно определить пустой метод в абстрактном классе и переопределить его?
Конечно, и вам даже не нужно определять метод в базовом классе. В 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
.
Методы Python всегда являются виртуальными.
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
На самом деле, в версии 2.6 python предоставляет нечто, называемое абстрактными базовыми классами, и вы можете явно устанавливать виртуальные методы, например:
from abc import ABCMeta
from abc import abstractmethod
...
class C:
__metaclass__ = ABCMeta
@abstractmethod
def my_abstract_method(self, ...):
Это работает очень хорошо, при условии, что класс не наследует от классов, которые уже используют метаклассы.
Методы 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
Методы Python всегда являются виртуальными.
... если они не являются частными! Слишком плохо для парня С++.
Что-то вроде виртуального метода в 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()
. Этот очень хороший доклад Раймонда Хеттингера дает пример для случая, когда это неудобно.