22--面向对象04:派生、组合、多态、鸭子类型
1 派生与方法重用
子类可以派生出自己新的属性
# 在子类派生的新方法中如何重用父类的功能
# 方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def f1(self):
print('%s say hello' %self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,sex,level,salary):
OldboyPeople.__init__(self,name,age,sex)
# 就是调用类的方法函数,必须按照函数 一一传参
self.level = level
self.salary=salary
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)
# 方式二:super()调用父类提供给自己的方法=》严格依赖继承关系
调用super()会得到一个特殊的对象,
该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性
# 调用的是绑定方法,自动传入self
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def f1(self):
print('%s say hello' %self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,sex,level,salary):
# super(Teacher,self).__init__(name,age,sex) # Python2 格式
super().__init__(name,age,sex) # 调用的是方法,自动传入当前self对象
self.level = level
self.salary=salary
# print(Teacher.mro())
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)
# super()案例
class A:
def test(self):
print('from A')
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
obj=C()
obj.test()
print(C.mro())
# 打印结果
from A
from B
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
class A:
def test(self):
print('from A')
super().test1()
class B:
def test(self):
print('from B')
class C(A,B):
def test1(self):
print('from C')
obj=C()
obj.test()
print(C.mro())
# 打印结果
from A
报错:super object has no attribute 'test1'
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
2 组合
# 在一个类中以另外一个类的实例化对象作为数据属性,称为类的组合。
# 组合与继承的区别:
1.组合与继承都是用来解决代码的重用性问题。
2.不同的是:
继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;
组合则是一种“有”的关系,比如老师有生日,老师有多门课程,
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合
# 示例
class Course:
def __init__(self,name,period,price):
self.name=name
self.period=period
self.price=price
def tell_info(self):
print('<%s %s %s>' %(self.name,self.period,self.price))
class Date:
def __init__(self,year,mon,day):
self.year=year
self.mon=mon
self.day=day
def tell_birth(self):
print('<%s-%s-%s>' %(self.year,self.mon,self.day))
class People:
school='清华大学'
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
# Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
class Teacher(People): # 老师是人,继承people类
def __init__(self,name,sex,age,title,year,mon,day):
super().__init__(name,age,sex)
self.birth=Date(year,mon,day) # 老师有生日
self.courses=[] # 老师有课程,可以在实例化后,往该列表中添加Course类的对象
def teach(self):
print('%s is teaching' %self.name)
python=Course('python','3mons',3000.0)
linux=Course('linux','5mons',5000.0)
teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)
# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)
# 重用Date类的功能
teacher1.birth.tell_birth()
# 重用Course类的功能
for obj in teacher1.courses:
obj.tell_info()
3 多态
3.1 多态介绍
# 什么是多态?
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪;水有 水蒸气、冰
class Animal: # 同一类事物:动物
def talk(self):
pass
class Cat(Animal): # 动物的形态之一:猫
def talk(self):
print('喵喵喵')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('汪汪汪')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print('哼哼哼')
# 实例化得到三个对象
>>> cat=Cat()
>>> dog=Dog()
>>> pig=Pig()
3.2 多态目的
# 为什么要有多态?
多态会带来多态性
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象
这就需要在设计时,把对象的使用方法统一成一种
# 例如:
cat、dog、pig都是动物,但凡是动物肯定有talk方法,
于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
cat.talk() # 喵喵喵
dog.talk() # 汪汪汪
pig.talk() # 哼哼哼
更进一步,我们可以定义一个统一的接口(函数)来使用:
def Talk(animal):
animal.talk()
Talk(cat) # 喵喵喵
Talk(dog) # 汪汪汪
Talk(pig) # 哼哼哼
# 多态性的好处:在于增强了程序的灵活性和可扩展性
比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,
使用者根本无需关心obj具体是什么类,就可以使用相同的方式使用obj.talk()
# 综上我们得知:
多态性的本质在于不同的类中定义有相同的方法名
这样我们就可以不考虑类,而统一用同一种方式去使用对象
可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
# 抽象类
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
4 鸭子类型
# “鸭子类型”(duck typing):
其实我们完全可以不依赖于继承,只需要制造出外观和行为相同的对象,
同样可以实现不考虑对象类型而使用对象
“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。
比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度
# 示例
# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: # Txt类有两个与文件类型同名的方法,即read和write
def read(self):
pass
def write(self):
pass
class Disk: # Disk类也有两个与文件类型同名的方法:read和write
def read(self):
pass
def write(self):
pass