Python中纯虚函数是否可行或值得?

23 投票
2 回答
31750 浏览
提问于 2025-04-28 11:48

我可能是从一个不同的思维方式出发,因为我主要是个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>

撰写回答