以下主要是针对Python3中类的多继承、检查对象特性、类的多态介绍和示例。
多继承(Multiple Inheritance)
注意:不要和多重继承搞混,多重继承是指C继承B,B继承A这样的继承形式。
示例代码:(多重继承)
class A:
pass
class B(A):
pass
class C(B):
pass
多继承是指类能够继承自多个超类。
所以,在上一篇教程我们在使用__bases__特性时,能够看到bases是一个复数。
多继承的使用很简单,只需要在来名称后面的括号中写入超类的名称并用逗号分隔即可。
示例代码:(多继承)
class A:
pass
class B:
pass
class C(A,B):
pass
不过要注意,当多个超类都具有相同的特性时,只会继承第一个(最左侧)超类中的特性。
在现实生活中,有很多这种情况,例如小学生既是学生又是儿童。
就像下面这段代码。
示例代码:
class Children: # 创建儿童类
age = 10
def activitie(self): # 定义活动方法
print('我周末去儿童游乐园玩!')
class Student: # 创建学生类
grade = 3
def activitie(self): # 定义活动方法
print('我每天放学在家写作业!')
class Pupil(Children, Student): # 创建小学生类
def __init__(self):
print('我今年%d岁,已经上%d年级啦!' % (self.age, self.grade))
p = Pupil() # 显示输出结果为:我今年10岁,已经上3年级啦!
p.activitie() # 显示输出结果为:我周末去儿童游乐园玩!
检查对象的特性
如果想知道一个对象是否具有某个特性,可以使用hasattr(o,name)函数,当包含指定特性的名称时,返回值为True;否则,为False。
借用上面的学生类,我们进行特性的检查。
c = Children()
print(hasattr(c, 'age')) # 检查特性age,显示输出结果为:True
print(hasattr(c, 'grade')) # 检查特性grate,显示输出结果为:False
print(hasattr(c, 'activitie')) # 检查方法activitie,显示输出结果为:True
print(hasattr(c, 'study')) # 检查方法study,显示输出结果为:False
另外,我们还可以检查一个对象的特性是否能被调用。
我们可以使用getattr(o,name,default)获取对象的特性,然后通过callable(object)检查。
print(callable(getattr(c, 'activitie', None))) # 显示输出结果为:True
print(callable(getattr(c, 'study', None))) # 显示输出结果为:False
上方代码中,我们为getattr()函数指定了默认值None,这样当找不到特性时,返回值为False。
如果不设置默认值None,则会抛出异常。
AttributeError: ‘Children’ object has no attribute ‘study’
特性错误: ‘Children’ 对象不包含 ‘study’特性。
多态(Polymorphic)
多态的字面意思是多种形式。
在Python中是指多个不同类的对象,都具有一些共同的特性,这些对象中的任何一个对象,都可以调用这些共同的特性。但是,因为是不同类的对象,所以,在调用同一个特性时,会表现出不同的行为。
这里提到的对象允许外部访问的共同特性,也就是在前面我们提到过接口。
当我们处理多态对象时,只需要关心它所提供的接口。
例如,count()方法就是多态特性。
示例代码:
obj = '小明棒,小明帅,小明好厉害!' # 变量引用字符串对象
print(obj.count('小明')) # 显示输出结果为:3
obj = ('小明', '樱井', '小明', '明步') # 变量引用元组对象
print(obj.count('小明')) # 显示输出结果为:2
在上方代码中,“obj”就是多态对象,它可以是不同的对象。
我们不管“obj”是什么对象(字符串或元组或其它),只需要关心它的接口中有没有count()这个方法。
如果有count()这个方法,就能够对对象进行处理。
但是,虽然调用的方法都是同一个名称,因为处理的是不同的对象,实际上具体的处理行为是不一样的。
接下来,我引用一个其它编程语言中的示例,来帮助大家了解多态。
动物园的饲养员(Feeder ),能够喂养狮子(Lion),老虎(Tiger)和狗熊(Bear)。
饲养员要对这些动物进行喂食的操作。
假设我们不做多态处理。
示例代码:(非多态)
# 定义动物
class Lion: # 定义狮子类
def lion_eat(self): # 定义进食函数
print('狮子在吃东西!')
class Tiger: # 定义老虎类
def tiger_eat(self): # 定义进食函数
print('老虎在吃东西!')
class Bear: # 定义狗熊类
def bear_eat(self): # 定义进食函数
print('狗熊在吃东西!')
# 定义饲养员
class Feeder:
def feed_lion(self, lion): # 定义喂养狮子的函数
lion.lion_eat()
def feed_tiger(self, tiger): # 定义喂养老虎的函数
tiger.tiger_eat()
def feed_bear(self, bear): # 定义喂养猴子的函数
bear.bear_eat()
# 喂养过程
feeder = Feeder()
lion = Lion()
feeder.feed_lion(lion) # 显示输出结果为:狮子在吃东西!
tiger = Tiger()
feeder.feed_tiger(tiger) # 显示输出结果为:老虎在吃东西!
bear = Bear()
feeder.feed_bear(bear) # 显示输出结果为:狗熊在吃东西!
上方的代码虽然运行正常,但是如果给饲养员增加工作量,让他再多喂一只猴子。
我们就需要继续增加一些代码。
示例代码:(增加的代码)
#定义动物
class Monkey:
def monkey_eat(self):
print('猴子在吃东西!')
#定义饲养员
class Feeder:
def feed_monkey(self, monkey):
monke.monkey_eat()
# 喂养过程
monkey = Monkey()
feeder.feed_monkey(monkey)
很显然,每增加一个喂养一个动物,我们都要增加这么多代码。
那么,通过多态处理呢?
示例代码:(多态)
class Lion: # 定义狮子类
def eat(self): # 定义进食函数
print('狮子在吃东西!')
class Tiger: # 定义老虎类
def eat(self): # 定义进食函数
print('老虎在吃东西!')
class Bear: # 定义狗熊类
def eat(self): # 定义进食函数
print('狗熊在吃东西!')
class Feeder: # 定义饲养员类
def feed_animal(self, animal): # 定义喂食方法
animal.eat()
# 喂养过程
feeder = Feeder()
animal = Lion()
feeder.feed_animal(animal) # 显示输出结果为:我是狮子,在吃东西!
animal = Tiger()
feeder.feed_animal(animal) # 显示输出结果为:我是狮子,在吃东西!
animal = Bear()
feeder.feed_animal(animal) # 显示输出结果为:我是狗熊,在吃东西!
在上方代码中,每一个动物的进食方法都采用了相同的名称,喂食方法只保留了一个。
并且,喂养过程中调用的喂食方法都是相同的。(因为喂食方法就一个)
这样,多个类中都有一个相同的特性(eat函数),实例对象也就具有了这个相同的特性(eat方法),所以,不管调用的时候是哪一种实例对象(变量animal),都可以调用这个特性(eat方法),但是因为调用这个特性的实例对象(动物)不同,所以产生的行为(eat方法的处理过程)是不同的。
这就是多态。
那么,对比之前未做多态处理的代码,有什么好处呢?
当同样需要增加喂养猴子时,通过多态处理的代码,只需要增加新的动物种类,以及喂养过程中对动物的实例化和调用喂养方法。
示例代码:(增加部分)
#定义动物
class Monkey:
def eat(self):
print('猴子在吃东西!')
# 喂养过程
animal = Monkey()
feeder.feed_animal(animal)
从这个示例我们也能够看出,喂食方法无需再重复定义,多喂养一种动物只需要定义一个新的类。
这也就说明了,多态能够提高代码的复用性,让代码更加精简易读,并且降低了代码的耦合度(紧密配合与相互影响的程度),提升了代码的扩展性。
另外,值得注意的是,Python中多态的概念不同于其它编程语言,在大多编程语言中,如果需要进行多态处理,会要求具有相同特性的类(例如上方代码中的狮子类、老虎类等)必须继承自同一个超类(需要定义Animal类),这样在调用特性时是通过超类寻找相应的子类。
而Python中无需这么做,就像上方的代码,我们并没有定义一个动物的超类,让动物的子类去继承这个超类。
所以,严格来说Python中没有多态这个概念,因为Python自身就是多态的语言。