【发布时间】:2010-11-04 11:05:18
【问题描述】:
假设你有以下情况
#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);
}
如您所见,makeSpeak 是一个接受通用 Animal 对象的例程。在这种情况下,Animal 与 Java 接口非常相似,因为它只包含一个纯虚方法。 makeSpeak 不知道它通过的 Animal 的性质。它只是向它发送信号“speak”,并让后期绑定处理调用哪个方法:Cat::speak() 或 Dog::speak()。这意味着,就 makeSpeak 而言,知道实际传递了哪个子类是无关紧要的。
但是 Python 呢?让我们看看 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)
现在,在此示例中,您会看到相同的策略。您使用继承来利用 Dogs 和 Cats 都是 Animals 的分层概念。 但在 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 中,您可以将“说话”信号发送给您想要的任何对象。如果对象能够处理它,它将被执行,否则它会引发异常。假设您在两个代码中都添加了一个 Airplane 类,并将一个 Airplane 对象提交给 makeSpeak。在 C++ 的情况下,它不会编译,因为 Airplane 不是 Animal 的派生类。在 Python 的情况下,它会在运行时引发异常,这甚至可能是预期的行为。
另一方面,假设您添加了一个带有方法 speak() 的 MouthOfTruth 类。在 C++ 案例中,您必须重构层次结构,或者必须定义不同的 makeSpeak 方法来接受 MouthOfTruth 对象,或者在 java 中,您可以将行为提取到 CanSpeakIface 中并为每个对象实现接口。有很多解决方案...
我想指出的是,我还没有找到在 Python 中使用继承的单一理由(除了框架和异常树,但我想存在替代策略)。您无需实现基派生层次结构即可以多态方式执行。如果您想使用继承来重用实现,您可以通过包含和委托来完成相同的操作,另外还有一个好处是您可以在运行时对其进行更改,并且您可以清楚地定义包含的接口,而不会冒意外副作用的风险。
那么,最后的问题是:在 Python 中继承有什么意义?
编辑:感谢您提供非常有趣的答案。事实上,你可以将它用于代码重用,但我在重用实现时总是很小心。一般来说,我倾向于做非常浅的继承树或根本不做树,如果一个功能很常见,我将它重构为一个公共模块例程,然后从每个对象中调用它。我确实看到了单点更改的好处(例如,我只添加到 Animal,而不是添加到 Dog、Cat、Moose 等,这是继承的基本优势),但是您可以通过一个委托链(例如,一个 JavaScript)。我并不是说它更好,只是另一种方式。
在这方面我也找到了a similar post。
【问题讨论】:
-
-1:“您可以通过委托链实现相同的目的”。没错,但比继承要痛苦得多。您完全可以在不使用任何类定义的情况下实现相同的功能,只需使用大量复杂的纯函数即可。你可以用十几种方法来实现同样的事情,都比继承简单。
-
确实我说过“我不是说它更好;)”
-
“我还没有找到在 python 中使用继承的单一理由”...听起来确实像“我的解决方案更好”。
-
对不起,如果它给你这个印象。我的帖子旨在获得关于在 python 中使用继承的真实案例故事的积极反馈,截至今天,我无法找到(主要是因为在我的所有 python 编程中,我遇到了不需要这样做的情况,以及何时我做了,就是我上面解释的情况)。
-
现实世界的分类法很少成为面向对象示例的良好基础。
标签: python oop inheritance