Python中纯虚函数是否可行或值得?
我可能是从一个不同的思维方式出发,因为我主要是个C++程序员。这个问题和Python中的面向对象编程(OOP)有关,特别是关于纯虚方法的。这里有一段我从这个问题改编的代码示例。
class Animal():
def speak(self):
print("...")
class Cat(Animal):
def speak(self):
print("meow")
class Dog(Animal):
def speak(self):
print("woof")
my_pets = [Dog(), Cat(), Dog()]
for _pet in my_pets:
_pet.speak()
你可以看到它为不同的派生类调用了speak函数。现在我的问题是,鸭子类型很好,我觉得我已经理解了。但是,在Python中追求更严格的面向对象编程是错的吗?所以我查看了抽象基类,特别是abstractmethod。在我看来,这似乎只是让我能够用super调用基类的方法。有没有什么方法或理由(在Python中)让speak()
变成纯虚函数,以至于实现一个没有speak的派生动物会抛出错误?
我认为这样做的理由是,当你编写模块和框架,打算让别人去子类化时,这样可以自我说明他们需要实现这个函数。一个可能非常糟糕的想法是让基类的“纯”函数抛出异常。问题是,这个错误是在运行时才会被发现的!
class VirtualException(BaseException):
def __init__(self, _type, _func):
BaseException(self)
class Animal():
def speak(self):
raise VirtualException()
class Cat(Animal):
def speak(self):
print("meow")
class Dog(Animal):
def speak(self):
print("woof")
class Wildebeest(Animal):
def function2(self):
print("What!")
my_pets = [Dog(), Cat(), Dog(), Wildebeest()]
for _pet in my_pets:
_pet.speak()
2 个回答
11
问题是这个错误是在运行时出现的!
嗯,这就是Python……大多数错误都是在运行时出现的。
据我所知,在Python中,处理错误最常见的方式就是你所描述的:让基类的speak
方法抛出一个异常:
class Animal():
def speak(self):
raise NotImplementedError('You need to define a speak method!')
38
抽象基类已经可以满足你的需求了。abstractmethod
这个东西和你用 super
调用方法没有关系;其实你本来就可以这样做。它的作用是,任何被 abstractmethod
装饰的方法,子类必须重写这些方法,才能创建这个子类的实例:
Python 3:
>>> class Foo(metaclass=abc.ABCMeta):
... @abc.abstractmethod
... def foo(self):
... pass
...
>>> class Bar(Foo):
... pass
...
>>> class Baz(Bar):
... def foo(self):
... return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x00000210D702E2B0>
Python 2:
>>> class Foo(object):
... __metaclass__ = abc.ABCMeta
... @abc.abstractmethod
... def foo(self): pass
...
>>> class Bar(Foo): pass
...
>>> class Baz(Bar):
... def foo(self): return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x0000000001EC10B8>