什么&x2019;Python的继承点是什么?
假设您有以下情况什么&x2019;Python的继承点是什么?,python,oop,inheritance,Python,Oop,Inheritance,假设您有以下情况 #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 &
#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);
}
现在,在这个例子中,您可以看到相同的策略。你使用遗传来利用狗和猫都是动物的等级观念。
但是在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中,您可以向任何想要的对象发送信号“说话”。如果对象能够处理它,它将被执行,否则它将引发异常。假设您向这两个代码中添加了一个类飞机,并将一个飞机对象提交给makeSpeak。在C++的情况下,它不会编译,因为飞机不是一个派生类动物。在Python的情况下,它将在运行时引发异常,这甚至可能是预期的行为
另一方面,假设您添加了一个带有speak()方法的moutoftruth类。在C++的情况下,要么你必须重构你的层次结构,要么你必须定义一个不同的MaSkPosim方法来接受口述对象,或者在java中你可以把行为提取到一个CabScript中,并为每个接口实现接口。有很多解决方案
我想指出的是,我还没有找到在Python中使用继承的单一原因(除了框架和异常树之外,但我想还有其他策略)。无需实现基本派生层次结构即可执行多态性。如果您想使用继承来重用实现,那么您可以通过包含和委派来实现这一点,另外还有一个好处,即您可以在运行时修改它,并且您可以清楚地定义包含的接口,而不会产生意外的副作用 因此,最后的问题是:Python中的继承有什么意义 编辑:感谢您提供了非常有趣的答案。实际上,您可以将其用于代码重用,但在重用实现时,我总是很小心。一般来说,我倾向于做非常浅的继承树,或者根本不做树,如果某个功能是通用的,我将其重构为一个通用模块例程,然后从每个对象调用它。我确实看到了单点更改的优势(例如,我没有添加到狗、猫、驼鹿等,而是添加到动物,这是继承的基本优势),但您可以通过委托链(例如,la JavaScript)实现同样的优势。我并不是说这样更好,只是换一种方式而已
我也发现了这一点。我认为Python中的继承不是为了让代码编译,而是为了继承的真正原因,即将类扩展到另一个子类,并重写基类中的逻辑。然而,Python中的duck类型使“接口”概念变得无用,因为您可以在调用之前检查该方法是否存在,而无需使用接口来限制类结构。您将运行时duck类型称为“重写”继承,然而,我相信继承作为一种设计和实现方法有其自身的优点,是面向对象设计的一个组成部分。以我的拙见,您是否能够实现其他目标的问题并不重要,因为实际上您可以在不使用类、函数等的情况下编写Python代码,但问题是您的代码设计得有多好、健壮和可读性 我可以举两个例子来说明在我看来继承是正确的方法,我相信还有更多 首先,如果您明智地编写代码,makeSpeak函数可能希望验证其输入是否确实是动物,而不仅仅是“它会说话”,在这种情况下,最优雅的方法是使用继承。同样,您可以用其他方式来实现,但这就是面向对象设计与继承的美妙之处——您的代码将“真正”检查输入是否是“动物” 第二,显然更直接的是封装——面向对象设计的另一个组成部分。当祖先具有数据成员和/或非抽象方法时,这就变得相关了。举一个愚蠢的例子,在这个例子中,祖先有一个函数(说两遍)调用一个当时的抽象函数:
class Animal(object):
def speak(self):
raise NotImplementedError()
def speak_twice(self):
self.speak()
self.speak()
class Dog(Animal):
def speak(self):
print "woff!"
class Cat(Animal):
def speak(self):
print "meow"
假设“说两遍”
是一个重要的特性,您不想同时在Dog和Cat中对其进行编码,我相信您可以推断出这个示例。当然,您可以实现一个Python独立函数,该函数将接受一些duck类型的对象,检查它是否有speak函数并调用它两次,但这既不优雅,也忽略了第1点(验证它是动物)。更糟糕的是,为了加强封装示例,如果子类中的成员函数想要使用“说两遍”
,该怎么办
如果祖先类有一个数据成员,例如“腿的数量”
,它被祖先类中的非抽象方法使用,如“腿的数量”
,但在后代类的构造函数中启动(例如,Dog将用4初始化它,Snake将用0初始化它)
同样,我相信还有无数的例子,但基本上每一个(足够大的)基于面向对象设计的软件都需要继承。Python中的类基本上只是对一组函数和数据进行分组的方法。。它们与C++中的类和…不同。 我主要看到继承用于重写超类的方法。例如,可能更像Python的继承用法是
from world.animals import Dog
class Cat(Dog):
def speak(self):
print "meow"
当然猫不是狗的一种,但我有一个(第三方)dog
类,它工作得很好,除了我想覆盖的speak
方法之外——这节省了重新实现整个类的时间,就这样它发出喵喵声。同样,虽然猫不是狗的一种,但是猫确实继承了很多属性
重写方法或属性的更好(实用)示例
from world.animals import Dog
class Cat(Dog):
def speak(self):
print "meow"
import urllib
class AppURLopener(urllib.FancyURLopener):
version = "App/1.7"
urllib._urlopener = AppURLopener()
class AnimalError(Exception):
pass
class AnimalBrokenLegError(AnimalError):
pass
class AnimalSickError(AnimalError):
pass
command = raw_input("What do you want the dog to do?")
if command in dir(d): getattr(d,command)()
class Repeat:
"Send a message more than once"
def __init__(repeat, times, do):
repeat.times = times
repeat.do = do
def __call__(repeat):
for i in xrange(repeat.times):
repeat.do()
class Speak:
def __init__(speak, animal):
"""
Check that the animal can speak.
If not we can do something about it (e.g. ignore it).
"""
speak.__call__ = animal.speak
def twice(speak):
Repeat(2, speak)()
class Dog:
def speak(dog):
print "Woof"
class Cat:
def speak(cat):
print "Meow"
>>> felix = Cat()
>>> Speak(felix)()
Meow
>>> fido = Dog()
>>> speak = Speak(fido)
>>> speak()
Woof
>>> speak.twice()
Woof
>>> speak_twice = Repeat(2, Speak(felix))
>>> speak_twice()
Meow
Meow
class AnimalControl(object):
def __init__(self):
self._animalsInTruck=[]
def catachAnimal(self,animal):
if isinstance(animal,Animal):
animal.speak() #It's upset so it speak's/maybe it should be makesNoise
if not self._animalsInTruck.count <=10:
self._animalsInTruck.append(animal) #It's then put in the truck.
else:
#make note of location, catch you later...
else:
return animal #It's not an Animal() type / maybe return False/0/"message"