面向对象之继承
一、动静态方法
动态方法
1. 绑定给对象的方法
class Student: school_name = '摆烂大学' def func1(self): print('看谁最能摆烂 真的好棒棒!!!')
类中直接定义的函数,都是默认给对象使用的,类名加括号,产生一个对象:
obj = Student() # 产生一个对象
对象名.函数名,即可调用该函数,对象调用函数,默认把对象名作为第一个参数传给函数。上述类中的函数有一个参数,所以对象调用该函数不用传参。
obj.func1() # 看谁最能摆烂 真的好棒棒!!!
如果是类调用的话,就是有几个参数传几个参数:
Student.func1(a) # 看谁最能摆烂 真的好棒棒!!!
2. 绑定给类的方法: @classmethod
class Student: school_name = '摆烂大学' @classmethod def func2(cls): print('嘿嘿嘿 猜猜我是干嘛滴',cls)
被@classmethod修饰的函数,默认绑定给类。类调用则第一个参数就是类自身,对象也可以调用并且会自动将产生该对象的类当做第一个参数传入
Student.func2() # func2(Student) 嘿嘿嘿 猜猜我是干嘛滴 <class '__main__.Student'>
obj.func2() # func2(Student) 嘿嘿嘿 猜猜我是干嘛滴 <class '__main__.Student'>
静态方法:
普普通通的函数
@staticmethod def func3(a): print('哈哈哈 猜猜我又是什么', a)
不管谁来调用,必须传参,有几个参数传几个参数。
Student.func3(123) # 哈哈哈 猜猜我又是什么 123 obj.func3(321) # 哈哈哈 猜猜我又是什么 321
二、
面向对象三大特性
封装、继承、多态
- 三者中继承最为核心(实操最多,体验最强)
- 封装和多态略微抽象
继承的含义:
在现实生活中继承表示人与人之间资源的从属关系
eg:儿子继承父亲 干女儿继承干爹
在编程世界中继承表示类与类之间资源的从属关系
eg:类A继承类B
继承的目的:
在现实生活中儿子继承父亲,儿子就拥有了父亲所有资源的支配权限。
在编程世界中,类A继承类B,类A就拥有了类B中所有的数据和方法使用权限。
继承的实操:
1. 在定义类的时候类名后面可以加括号填写其他类名,意味着继承其他类
class Son(Father): pass
2. 在python支持多继承,括号内填写多个类名,彼此逗号隔开即可
class Son(F1, F2, F3): pass
上述中继承其他类的类——Son,我们称之为子类、派生类。
被继承的类——Father、F1、F2、F3,我们称之为父类、基类、超类。
我们最常用的就是子类和父类。
继承本质应该分为两部分:
抽象: 将多个类相同的东西抽出去形成一个新的类
继承: 将多个类继承刚刚抽取出来的新的类
要找出类与类之间的继承关系,需要先抽象,再继承。抽象即总结相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类,如下图所示:
基于抽象的结果,我们就找到了继承关系:
class Student: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender def choice_course(self): print(f'{self.name}正在选课') class Teacher: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender def teach_course(self): print(f'{self.name}正在教课')
上述写法会造成代码冗余,为了节省代码的考虑,可以将相同的部分抽取出来形成一个新的类,然后让学生类和老师类都继承这个新的类。
class Person: def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class Student(Person): def choice_course(self): print(f'{self.name}正在选课') class Teacher(Person): def teach_course(self): print(f'{self.name}正在教课') stu1 = Student('jason', 18, 'male') tea1 = Teacher('tony', 29, 'others')
- 对象:数据与功能的结合体
- 类(子类):多个对象相同数据和功能的结合体
- 父类:多个类(子类)相同数据和功能结合体
ps: 类与父类本质都是为了节省代码
三、
1.先从自己的名称空间中查找
2.自己没有再去产生该对象的类中查找
3.如果类中也没有 那么直接报错
class C1: name = 'jason' def func(self): print('from func') obj = C1() print(C1.name) # jason obj.name = 'kevin' # 由于对象原本没有name属性,该语法会在对象名称空间中创建一个新的'键值对' print(obj.name) # kevin 先从自己的名称空间中查找 print(C1.name) # jason
单继承情况下名字的查找顺序:
对象自身 >>> 产生对象的类 >>> 父类
案例1:
先从自身查找:
class F1: name = 'jason' class S1(F1): name = 'kevin' obj = S1() obj.name = 'oscar' print(obj.name) # oscar
自身没有,则从产生对象的类中查找:
class F1: name = 'jason' class S1(F1): name = 'kevin' obj = S1() print(obj.name) # kevin
自身和产生对象的类中都没有,则往上从产生对象的类的父类中查找:
class F1: name = 'jason' class S1(F1): pwd = 123 obj = S1() print(obj.name) # jason
案例2:
class F3: name = 'jerry' class F2(F3): name = 'tony' class F1(F2): name = 'jason' class S1(F1): name = 'kevin' obj1 = S1() obj1.name = '嘿嘿嘿' print(obj1.name)
查找顺序:obj1 >>> S1 >>> F1 >>> F2 >>> F3
案例3:
class A1: def func1(self): print('from A1 func1') def func2(self): print('from A1 func2') self.func1() class B1(A1): def func1(self): print('from B1 func1') obj = B1() obj.func2()
第一步:先从产生对象的类B1中查找,B1中没有func2,然后从B1的父类A1中查找,A1中有func2,所以先打印'from A1 func2';
第二步:之后self.func1(),再重新回到对象自身,先从对象自身查找,对象中没有,所以从产生对象的类B1中查找,打印'from B1 func1'
强调: 对象点名字,永远从对象自身开始一步步查找
以后在看到self.名字的时候,一定要搞清楚self指代的是哪个对象
多继承情况下名字的查找顺序:
1. 非菱形继承
(1)多个父类,第一个父类不再有其他父类:按照括号里的父类从左往右
class F1: name = 'jason' class F2: name = 'oscar' class F3: name = 'jerry' class S1(F1, F2, F3): name = '嘿嘿嘿' obj = S1() obj.name = '想干饭' print(obj.name)
对象自身 >>> 产生对象的类 >>> 父类(从左往右)
obj >>> S1 >>> F1 >>> F2 >>> F3
(2) 多个父类,每个父类还有父类
深度优先(从左往右每条道走完为止)
class A: # name = 'from A' pass class B: # name = 'from B' pass class C: # name = 'from C' pass class D(A): # name = 'from D' pass class E(B): # name = 'from E' pass class F(C): # name = 'from F' pass class S1(D,E,F): pass obj = S1() print(obj.name)
每条线走完再进行下一条:
obj >>> S1 >>> D >>> A>>> E >>> B >>> F >>> C
2. 菱形继承:
多个父类,每个父类还有父类,且最后形成一个闭环
广度优先(最后才会找闭环的定点)
class G: name = 'from G' class A: name = 'from A' class B: name = 'from B' class C: name = 'from C' class D(A): name = 'from D' class E(B): name = 'from E' class F(C): name = 'from F' class S1(D,E,F): pass obj = S1() print(obj.name)
先把每条线和广度铺开,最后一条回归到闭环:
obj >>> S1 >>> D >>> A>>> E >>> B >>> F >>> C >>> G
如果实在捋不清名字的查找顺序,可以通过mro方法来判断:
print(S1.mro())
# 可以得到一个类的名字列表,列表里面类的先后顺序就是查找的先后顺序: # [<class '__main__.S1'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class 'object'>]
四、 经典类与新式类
经典类: 不继承object或者其子类的类,要使用object或其子类,必须自己添加object。经典类已成过去式,现在基本不用
class Student(object):pass
新式类: 继承object或者其子类的类,可以默认使用object里的所有类
在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object)
ps:以后我们在定义类的时候,如果没有其他明确的父类,也可能习惯写object兼容