假设您有以下情况
#include <iostream>
class Animal {
public:
virtual void speak() = 0;
};
class Dog : public Animal {
void speak() { std::cout << "woff!" <<std::endl; }
};
class Cat : public Animal {
void speak() { std::cout << "meow!" <<std::endl; }
};
void makeSpeak(Animal &a) {
a.speak();
}
int main() {
Dog d;
Cat c;
makeSpeak(d);
makeSpeak(c);
}
正如您所看到的,makspeak 是一个接受泛型 Animal 对象的例程。在本例中,Animal 非常类似于 Java 接口,因为它只包含一个纯虚方法。MakeTalk 不知道它所传递的动物的性质。它只是向它发送信号“ speak”,并留下后期绑定来处理要调用哪个方法: Cat: : speak ()或 Dog: : speak ()。这意味着,就 makeTalk 而言,实际传递哪个子类的知识是不相关的。
那巨蟒呢?让我们看看 Python 中相同案例的代码。请注意,我试图尽可能地与 C + + 类似:
class Animal(object):
def speak(self):
raise NotImplementedError()
class Dog(Animal):
def speak(self):
print "woff!"
class Cat(Animal):
def speak(self):
print "meow"
def makeSpeak(a):
a.speak()
d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)
现在,在这个例子中你看到了相同的策略。您可以使用继承来利用狗和猫都是动物的等级概念。 但是在 Python 中,不需要这个层次结构,它同样可以很好地工作
class Dog:
def speak(self):
print "woff!"
class Cat:
def speak(self):
print "meow"
def makeSpeak(a):
a.speak()
d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)
在 Python 中,您可以将信号“ speak”发送到任何您想要的对象。如果对象能够处理它,它将被执行,否则将引发异常。假设您在两个代码中都添加了一个类 Airplane,并提交了一个 AirFlight 对象来 make Talk。在 C + + 的情况下,它不会编译,因为 Aircraft 不是 Animal 的派生类。在 Python 情况下,它将在运行时引发异常,这甚至可能是预期的行为。
另一方面,假设您添加了一个 MouthOfTruth 类和一个 speak ()方法。在 C + + 的例子中,要么你必须重构你的层次结构,要么你必须定义一个不同的 makTalk 方法来接受 MouthOfTruth 对象,或者在 java 中你可以把行为提取到一个 CanSpikIface 中并为每个对象实现接口。有很多解决办法。
我想指出的是,我还没有找到在 Python 中使用继承的单一原因(除了框架和异常树之外,但是我猜还有其他策略存在)。您不需要实现基本派生的层次结构来执行多态性。如果您希望使用继承来重用实现,那么您可以通过包含和委托来实现同样的功能,还有一个额外的好处,那就是您可以在运行时修改它,并且您可以清楚地定义包含的接口,而不会产生意想不到的副作用。
因此,最后,问题仍然存在: 在 Python 中继承的意义是什么?
编辑 : 感谢您非常有趣的回答。实际上,您可以将它用于代码重用,但是在重用实现时我总是很小心。一般来说,我倾向于做非常浅的继承树,或者根本不做继承树,如果一个功能是通用的,我将它重构为一个通用的模块例程,然后从每个对象调用它。我确实看到了单点更改的好处(例如,不添加 Dog、 Cat、 Moose 等,而是添加 Animal,这是继承的基本优势) ,但是你可以通过代理链(例如,la JavaScript)实现同样的效果。我不是说这样更好,只是换种方式。
在这方面我也找到了 类似的职位。