OOP主要好处之一是代码的重用,其一是通过继承。

继承:继承是base class类与subclass的父&子类的关系,儿子会继承爸爸的属性和方法。

Eg:

动物类为父类, 是所有动物的基类;猫类继承于动物类,是动物类的子类和派生类。

继承有单继承与多继承。 单继承即子类继承于一个类,多继承即子类继承于多个类。

继承使用场合:假如我要定义几个类,而类与类之间有一些公共的属性和方法,这时就可以把相同的属性和方法定义在基类中;各个实例独有的方法和属性在实例化后的本类中定义。这样子类只需要继承基类(父类),子类就可以访问基类的属性和方法了。

一、Python类的继承

定义一个Animal为基类.

class Animal(object): # 基本所有类都继承于object基类
def __init__(self, name, age):# 实例属性name和age,一个method叫cal
self.name = name
self.age = age
def call(self):
print(self.name, 'Meow')
class Cat(Animal): # 定义一个Cat类继承于Animal类,Cat类比Animal类多一个sex属性。
def __init__(self,name,age,sex):
super(Cat, self).__init__(name,age) # 记得从Animal类引入name和age属性
self.sex=sex

if __name__ == '__main__': # 单模块被引用时下面代码不会受影响,用于调试

c = Cat('Kitty', 2, 'male') # Cat类继承了父类Animal的属性,并且加入自己独特的属性sex

c.call() # 输出 Kitty 'Meow',Cat继承了父类Animal的 call methodTips:super(Cat, self).__init__(name,age) 去初始化父类,继承父类的 name和age两个属性。

super(Cat, self)返回当前类继承的父类,即 Animal类,然后调用__init__()方法(此时self参数已在super()中传入,在__init__()中将隐式传递,不用再写出self)。

二、Python 对子类方法的重构

当猫类 Cat 有自己的叫声 'prefer MMMEEEEEOW' ,可以对父类的method call() 重构:

class Cat(Animal):
def __init__(self, name, age, sex):
super(Cat, self).__init__(name,age)
self.sex = sex
def call(self):
print(self.name,'prefer MMMEEEEEOW')# rebuild
if __name__ == '__main__':
c = Cat('Kitty', 2, 'male')

c.call() # 输出:Kitty prefer MMMEEEEEOW 为子类定义的method call()在子类中重构父类的method后,Cat子类的实例先会在自己的类 Cat 中查找该方法,当找不到该方法时才会去父类 Animal 中查找对应的方法。

三、Python中子类与父类的关系

class Animal(object):
pass
class Cat(Animal):
pass
A= Animal()
C = Cat()

我们可以说:

“A”是 Animal 类的实例,但,“A”不是 Cat 类的实例。

“C”是 Animal 类的实例,“C”也是 Cat 类的实例。

拓展:isinstance() 判断变量类型

判断对象之间的父子关系,通过 isinstance (变量,类名) :

print('"A" IS Animal?', isinstance(A, Animal))
print('"A" IS Cat?', isinstance(A, Cat))
print('"C" IS Animal?', isinstance(C, Animal))
print('"C" IS Cat?', isinstance(C, Cat))

输出:

"A" IS Animal? True
"A" IS Cat? False
"C" IS Animal? True
"C" IS Cat? Trueisinstance() 不只可以用在我们自定义的类,也可以用于判断变量的类型,如判断数据类型是否为 int、str、list、dict 等。
print(isinstance(100, int))
print(isinstance('100', int))
print(isinstance(100, str))
print(isinstance('100', str))

输出:

True
False
False
True

四、Python 多态

类有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Animal 派生出 Cat和 Dog,并都写了一个 call() 方法,如下示例:

class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def call(self):
print(self.name, '会叫')
class Cat(Animal):
def __init__(self, name, age, sex):
super(Cat, self).__init__(name, age)
self.sex = sex
def call(self):
print(self.name, '会“喵喵”叫')
class Dog(Animal):
def __init__(self, name, age, sex):
super(Dog, self).__init__(name, age)
self.sex = sex
def call(self):
print(self.name, '会“汪汪”叫')

定义一个 do 函数,接收一个变量 ‘all’,如下:

def do(all):
all.call()
A = Animal('小黑',4)
C = Cat('喵喵', 2, 'male')
D = Dog('旺财', 5, 'female')
for x in (A,C,D):
do(x)
输出:
小黑 会叫
喵喵 会“喵喵”叫
旺财 会“汪汪”叫

这种行为称为多态。方法调用作用在 all 的实际类型上。C 是 Cat 类型,它拥有自己Cat类内的 call() 方法+从 Animal 类继承的 call 方法。调用 C .call() 先去找它自身定义的method;如果没有定义,则顺着继承链向上查找,直到在某个父类中找到这个method为止。

传递给函数 do(all) 的参数 all 不一定是 Animal 或 Animal 的子类型。任何数据类型的实例object都可以,只要它有一个 call() 的方法即可。其他不继承于 Animal类,具备 call 方法的也可以使用 do 函数。

这就是动态语言,动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。

五、Python类继承 注意事项:在继承中基类的构造方法(__init__())不会被自动调用,它需要在其派生类的构造方法中亲自专门调用。

在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量; 在类中调用普通method时不需要带上self参数

Python 总是首先查找对应类的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。