Какой смысл наследования в Python?

Предположим, у вас есть следующая ситуация

<code>#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);
}
</code>

Как видите, makeSpeak - это подпрограмма, которая принимает универсальный объект Animal. В этом случае Animal довольно похож на интерфейс Java, поскольку содержит только чисто виртуальный метод. makeSpeak не знает природу Животного, которого ему передают. Он просто посылает ему сигнал & # x201C; speak & # x201D; и оставляет позднюю привязку, чтобы позаботиться о том, какой метод вызывать: Cat :: speak () или Dog :: speak (). Это означает, что для makeSpeak знание о том, какой подкласс фактически передан, не имеет значения.

Но как насчет Python? Давайте посмотрим код для того же случая в Python. Обратите внимание, что я стараюсь быть как можно более похожим на случай C ++:

<code>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)
</code>

Теперь в этом примере вы видите ту же стратегию. Вы используете наследование, чтобы использовать иерархическую концепцию, согласно которой собаки и кошки являются животными. Но в Python такая иерархия не нужна. Это работает одинаково хорошо

<code>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)
</code>

В Python вы можете отправить сигнал & # x201C; говорить & # x201D; на любой объект, который вы хотите. Если объект может с ним справиться, он будет выполнен, в противном случае он вызовет исключение. Предположим, вы добавили класс Airplane в оба кода и отправили объект Airplane в makeSpeak. В случае C ++ он не будет компилироваться, поскольку Airplane не является производным классом Animal. В случае с Python это вызовет исключение во время выполнения, что может быть даже ожидаемым поведением.

С другой стороны, предположим, что вы добавили класс MouthOfTruth с методом speak (). В случае C ++ вам придется либо реорганизовать свою иерархию, либо вам придется определить другой метод makeSpeak для приема объектов MouthOfTruth, либо в Java вы можете извлечь поведение в CanSpeakIface и реализовать интерфейс для каждого из них. Есть много решений ...

Я хотел бы отметить, что я не нашел единственной причины для использования наследования в Python (кроме каркасов и деревьев исключений, но я предполагаю, что существуют альтернативные стратегии). Вам не нужно реализовывать базовую производную иерархию для полиморфной работы. Если вы хотите использовать наследование для повторного использования реализации, вы можете сделать то же самое путем сдерживания и делегирования, с дополнительным преимуществом, которое вы можете изменять во время выполнения, и вы четко определяете интерфейс содержимого, не рискуя непредвиденными побочными эффектами.

Итак, в конце концов, возникает вопрос: в чем смысл наследования в Python?

EditСпасибо за очень интересные ответы. На самом деле вы можете использовать его для повторного использования кода, но я всегда осторожен при повторном использовании реализации. В общем, я склонен создавать очень мелкие деревья наследования или вообще не создавать дерево, и если функциональность является общей, я реорганизую ее как обычную процедуру модуля, а затем вызываю ее из каждого объекта. Я вижу преимущество наличия одной единственной точки изменения (например, вместо добавления к Dog, Cat, Moose и т. Д. Я просто добавляю к Animal, что является основным преимуществом наследования), но вы можете достичь того же с помощью цепочка делегирования (например, а-ля JavaScript). Я не утверждаю, что это лучше, просто другой способ.

Я также нашеланалогичный пост на этот счет.

Ответы на вопрос(11)

Ваш ответ на вопрос