继承
1.继承介绍
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
1.1.什么是继承
继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类
python中继承的特点:
1.2.为何要用继承
1.3.如何用继承
语法
class 父类:
pass
class 子类(父类):
pass
实例
class Parent1:
xxx = 333
def run(self):
print('我是父类的方法')
class Sub1(Parent1):
# xxx = 222
# def run(self):
# print('我是子类的方法')
pass
obj = Sub1()
# obj.xxx=111
# 对象 << 类 << 父类
print(obj.xxx)
obj.run()
2.利用继承来解决类和类直接的代码冗余问题
我们先思考一个问题:请看图
1.对象和类的关系
奥巴马和梅西都有人的特征,麦兜和猪坚强都有猪的特征,史努比和史派克都有狗的特征
所以总结对象的相似之处得到了类
2.类和父类的关系
例子
学生类和老师类都是人,所以他们具有人相同的特征
# @Author : 大海
# @File : 2.利用继承来解决类和类之间的代码冗余问题.py
'''
总结对象的相似之处得到了类
总结类的相似之处得到父类
'''
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
# def __init__(self,name,age,sex):
# self.name = name
# self.age = age
# self.sex = sex
def play(self):
print('%s play football' % self.name)
class Teacher(People):
# def __init__(self,name,age,sex):
# self.name = name
# self.age = age
# self.sex = sex
def course(self):
print('%s course'%self.name)
# 实例化的时候子类没有__init__方法会调用父类的
stu1 = Student('周阳',30,'male')
print(stu1.__dict__)
tea1 = Teacher('大海',31,'man')
print(tea1.__dict__)
# 但是这里有个问题子类有新的属性需要实例化的时候参数怎么办
继承是一种新建类的方式,新建的类称之为子类/派生类,被继承 的类称之为父类\基类\超类
python中继承的特点: 子类可以遗传/重用父类的属性
派生
1.什么是派生
指的是子类继承父类的属性和方法,并且派生出自己独有的属性与方法。
2.为什么要派生
子类可以派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,
例如每个学生还有分数这一属性,我们就需要在Student类中定义该类自己的
init方法去覆盖父类的(因为优先用子类的方法)
子类派生重用父类功能一
子类重用父类的功能,在子类派生出的新方法中重用父类功能的方式一:指名道姓地引用某个类中的函数。
class People:
school='图灵学院'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Student(People): #这里即使没有引用父类,代码也能下常运行
#写个默认参数
def __init__(self,name,age,sex,score=0):
People.__init__(self,name,age,sex) #指明方法 引用并修改
self.score=score
def play(self):
print('%s play football'%self.name)
class Teacher(People): # 子类在父类的基础上添加一个新的形参
def __init__(self,name,age,sex,hoppy):
People.__init__(self,name,age,sex)
self.hoppy=hoppy
def course(self):
print('%s course'%self.name)
stu1=Student('周阳',30,'male')
print(stu1.__dict__)
tea1=Teacher('大海',31,'男','篮球')
print(tea1.__dict__)
子类派生重用父类功能二
派生实例化除了父类的属性添加,还能有自己独有的属性
在子类派生出的新方法中重用父类功能的方式二:super()必须在类中用
super(自己的类名,自己的对象)
可以省略传值
super()
class People:
school='图灵学院'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class Student(People):
#写个默认参数
def __init__(self,name,age,sex,score=0):
super(Student,self).__init__(name,age,sex)
# 也可省略传值 super().__init__(name,age,sex)
self.score=score
单继承背景下的属性查找
4.1.在单继承背景下的属性查找顺序
# @Author : 大海
# @File : 4.在单继承背景下的属性查找.py
# 在单继承背景下属性的查找优先级:对象->对象的类->父类->父类.....->object
class Foo():
xxx = 444
pass
class Bar1(Foo):
xxx = 333
pass
class Bar2(Bar1):
xxx = 222
pass
obj = Bar2()
# obj.xxx=111
print(obj.xxx)
例子
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
# obj.f1()是Bar()的对象
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj=Bar()
obj.f2()
多继承背景下的属性查找
广度优先查找
例子
# @Author : 大海
# @File : 5.在多继承背景下的属性查找.py
# 在多继承背景下属性的查找优先级: **
# 此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去
# 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
# 第四层 # 顶级类是在最后一个分之才走的
class G():
x = 'G'
pass
# 第三层
class E(G):
# x = 'E'
pass
class F(G):
# x='F'
pass
# 第二层
class B(E):
# x= "B"
pass
class C(F):
# x='C'
pass
class D(G):
# x='D'
pass
# 第一层
class A(B,C,D):
# x = 'A'
pass
obj = A()
# obj.x = 111
print(obj.x)
# python专门为继承类内置了一个mro的方法,用来查看c3算法的计算结果
print(A.mro())
优先级:对象》对象的类》按照从左往右的顺序一个分支一个分支的找下去
广度优先查找,从左往右一个分支一个分支的查找, 在最后一个分支才去查找 顶级 类
在多继承背景下的属性查找
mro方法
python 专门为继承类内置了一个mro的方法,用来查看c3算法的计算结果(查看子类继承属性的优先级)
print(A.mro()) #A为一子类,这里主要展示mro方法格式
组合
组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象
为何使用组合:
通过为某一个对象添加属性(属性的值 是另外 一个类的对象)的方式,可以间接地将两个类关联、整合、组合到一起,从而减少类与类之间代码的冗余。
class Foo:
xxx=111
class bar:
yyy=222
def zzz(self):
print('bar的方法')
obj=Foo()
obj1=bar()
#obj.attr='大海’ --->添加新值,创建属性
#print(obj.attr)
obj.attr=obj1 #---->添加对象,进行组合
# 组合在一起,obj.attr 等价于obj1
print(obj.xxx)
print(obj.attr.yyy)
obj.attr.zzz()
class People:
school = '图灵学院'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Student(People):
def __init__(self, name, age, sex,score=0):
super().__init__(name, age, sex)
self.score = score
class Teacher(People):
def __init__(self,name,age,sex,course=[]):
super().__init__(name, age, sex)
self.course=course
def tell_info1(self):
# 这个时候是列表里面装了4个课程对象
# print(self.course)
for i in self.course:
# print(i)
# 课程属性
# print('<课程名:%s,价格%s>' % (i.c_name, i.c_price))
# 课程方法
i.tell_info()
class Course:
def __init__(self,c_name,c_price):
self.c_name = c_name
self.c_price = c_price
def tell_info(self):
print('<课程名:%s,价格%s>'%(self.c_name,self.c_price))
python = Course('python开发',10000)
math = Course('高等数学',3000)
English = Course('英语',4000)
music = Course('音乐',5000)
drawing = Course('绘画',6000)
dahai = Teacher('大海',24,'man')
# print(dahai)
# 把music课程对象添加到老师对象tea的属性里面
# dahai.course = music
# print(dahai.course)
# print(music)
# dahai.course.tell_info()
# dahai.course = math
# dahai.course.tell_info()
# 如果我想把一堆的对象放到一个对象属性里面,我们怎么做?
# 思考一下? 放到一个容器里面 列表
# print(dahai.course)
# dahai.course = music
# print(dahai.course)
dahai.course.append(python)
dahai.course.append(math)
dahai.course.extend([English,music])
# 这个时候是列表里面装了4个课程对象
print(dahai.course)
print('===================')
dahai.tell_info1()
组合和继承的区别:
继承是把全部的属性和方法让子类可以调用,而组合只是部分的属性和方法把2个类关联到一起,
有些类的属性不能全部继承,这就用到了组合,组合的是对象,而继承 的是,类
继承 是在类定义产生的,它是类之间的关联关系,
而组合是类定义后产生的关系,因为它是对象的关联关系
8.多态
8.1.什么是多态
多态指的是同一种/类事物的不同形态,比如动物有多种形态:猫,狗,猪。
8.2.为何要用多态
多态性:在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象
多态性的精髓:统一
8.3.如何用多态
1.定义一个多态性的方法
class Animal:
# 动物都会叫
def speak(self):
print('我是动物我会说话')
class People(Animal):
def speak(self):
print('say hello')
class Dog(Animal):
def speak(self):
print('汪汪汪')
class Pig(Animal):
def speak(self):
print('哼哼哼')
# 三个对象都是动物
obj1=People()
obj2=Dog()
obj3=Pig()
2.多态性的精髓:统一
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类型,实例化得到的对象obj,可以使用相同的方式使用object.speak()
# 多态性的精髓:统一
# 学车,学的是小汽车标准,会了所有的小汽车都会开
obj1.speak()
obj2.speak()
obj3.speak()
# 内置方法多态方法
# len()
# 计算多个值的长度
3.python中一切皆对象,数据类型本身就是对象,本身就支持多态性
print(len([1, 2, 3, 4]))
print(len('1234'))
print(len((1, 2, 3, 4)))
# 计算多个值的长度,如果按照以下这样就缺乏了多态性,灵活性就弱
# size()
# zhangdu()
# kuangdu()
9.常用的魔法方法
9.1. str
在对象被打印时自动触发,可以用来定义对象被打印时的输出信息
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return '<name:%s age%s>'%(self.name,self.age)
def run(self):
return self.name
obj = People('大海',18)
print(obj)
print(obj.run())
9.2. del
在对象被删除时先自动触发该方法,可以用来回收对象以外其他相关资源,比如系统资源
# # __del__
# import time
class Foo:
def __init__(self,x,filepath,encoding='utf8'):
self.x=x
self.f=open(filepath,'rt',encoding=encoding)
def __del__(self):
print('__del__正在运行')
self.f.close()
obj = Foo(1,'a.txt')
# # print(obj.f.read())
# # obj.f.close()
# print('不是对象生成运行__del__,对象正在使用的时候不会运行')
# time.sleep(3)
# 对象使用不会运行__del__
# print(obj.f.read())
9.3. call
在对象被调用时会自动触发该方法
# __call__: 在对象被调用时会自动触发该方法
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print(self,args,kwargs)
def run(self):
print('run')
obj1 = Foo(1,2)
obj1.run()
# 没写__call__不可以调用
# obj1()
# 写了__call__可以调用
# obj1()
# obj1(1,2,x=3,y=4)
# # 省略了
# obj1.__call__(1,2,x=3,y=4)