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 总是首先查找对应类的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。