1.多态
概念:一种事物具备多种不同的形态
例如:水有三种形态
面向对象中的多态:多个不同类对象可以响应同一个方法,产生不同的结果
多态不是一种特殊的语法,而是一种状态,特性(多个对象有相同的使用方法)
优点:对于使用者,可以降低使用难度
实现多态
接口,抽象类,鸭子类型都可以实现多态,最简单的就是鸭子类型
鸭子类型实现多态例子
1 # 鸭子类型产生几个类 2 class Ji: 3 def bark(self): 4 print('咕咕咕') 5 6 def spawn(self): 7 print('下鸡蛋') 8 9 class Duck: 10 def bark(self): 11 print('嘎嘎嘎') 12 13 def spawn(self): 14 print('下鸭蛋') 15 16 17 class E: 18 def bark(self): 19 print('鹅鹅鹅') 20 21 def spawn(self): 22 print('下鹅蛋') 23 24 # 定义一个调用他们的方法 25 def manage(obj): 26 obj.bark() 27 obj.spawn() 28 29 j = Ji() 30 y = Duck() 31 e = E() 32 33 manage(j) # 多个不同的对象都可以使用manage的方法,产生不同的结果 34 manage(y) 35 manage(e)
在python中许多内置方法也是多态的表现形式
1 # python中很多内置方法也是多态的表现形式 2 a = 1 3 b = 'sxc' 4 c = [1,5,'xxx'] 5 # type,不同的对象拥有相同的方法,print其实也是多态的表现 6 print(type(a)) 7 print(type(b)) 8 print(type(c))
2.OOP相关的内置函数
(1)isinstance(obj,cls)
检查是否obj是类cls的对象,判断一个对象是否是某个类的实例
1 # isinstance(obj,cls) 2 class Person: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 p1 = Person('sxc',18) 8 9 p2 = ('sxc',18) 10 11 print(isinstance(p1,Person)) # 判断是否是该类的对象 12 print(isinstance(p2,Person)) 13 14 print(isinstance(p2[0],str)) # 还可以判断是否为基本类型,p2的第一个参数是'sxc' 15 print(isinstance(p2[1],int)) # p2的第二个参数是18,是int类型
(2)issubclass(sub,super)
检查sub类是否是super的子类(派生类),判断一个类是否是另一个类的子类
1 # issubclass(sub,super) 2 class Animal: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 class Person(Animal): 8 def speak(self): 9 print('人说话') 10 11 class Student(Person): 12 def study(self): 13 print('学生学习') 14 15 class Dog(Animal): 16 def bark(self): 17 print('狗叫') 18 19 print(issubclass(Student,Animal)) # 判断是否为某一个类的子类 20 print(issubclass(Dog,Animal)) # 是 21 print(issubclass(Student,Person)) # 是 22 print(issubclass(Dog,Person)) # 否
3.类中的魔法函数
(1)__str__
执行时机:会在对象被转换成字符串时执行,转换的结果就是这个函数的返回值
使用场景:可以利用该函数来自定义对象的打印格式
1 # __str__ 2 class Person: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 def __str__(self): # 自定义对象的打印格式 8 return '姓名:%s,年龄:%s'%(self.name,self.age) 9 10 p1 = Person('sxc',18) 11 print(p1) # 如果没有__str__函数返回值是该对象的内存地址.<__main__.Person object at 0x000001C339547278> 12 print(p1) # 利用该函数获取自己想要的返回值.姓名:sxc,年龄:18
作业:1.编写class类,拥有name,teacher,cursor三个属性,自定义打印格式输出,对象的全部属性
1 class School: 2 def __init__(self,name,teacher,course): 3 self.name = name 4 self.teacher = teacher 5 self.course = course 6 7 def __str__(self): 8 return '学校名:%s,老师名:%s,课程名:%s'%(self.name,self.teacher,self.course) 9 10 s1 = School('oldboy','tank','python10期') 11 print(s1)自定义打印格式输出
(2)__del__析构函数
执行时机:手动删除对象时立马执行,或是程序运行结束时也会执行
使用场景:当对象在使用过程中,打开了不属于解释器的资源:例如文件,网络端口等
1 # __del__ 2 class FileTool: 3 def __init__(self,path): 4 self.file = open(path,'r',encoding='utf-8') 5 6 def read(self): # 打开文件之后py解释器不会主动的关闭文件 7 return self.file.read() 8 9 def __del__(self): # 该方法在程序运行完毕之后自动执行 10 self.file.close() # 通过上述条件,确定我们不再复用该文件,可以在这关闭文件 11 print('关闭文件') # 证明程序最后会执行该方法 12 13 tool = FileTool('a.txt') 14 print(tool.read())
(3)__call__
执行时机:在调用对象时自动执行(即对象名加括号)
1 # __call__ 2 class A: 3 def __call__(self, *args, **kwargs): # 在调用对象时执行 4 print('运行call') 5 print(args) # 调用对象时传入的参数,单个参数 6 print(kwargs) # 传入的字典类型 7 8 a = A() 9 # a() 10 # a(18) 11 a(18, name = 'sxc') # 对象名+()自动触发
(4)__slots__优化内存
该属性是一个类属性,用于优化内存占用
优化的原理:将原本不固定的属性数量变的固定,从而达到减少内存开销的效果
需要注意的是这个方法会把__dict__都取消,并且当使用这个方法时,将导致这个类的对象无法添加新的属性
1 # __slots__ 2 import sys 3 4 class A: 5 __slots__ = ['name'] # 在声明属性时使用,中括号内的就是生成的属性, 6 # 并且只会产生括号中属性的名称空间 7 8 def __init__(self,name): 9 self.name = name 10 print(name) 11 # print(self.__dict__) # 内部也不能查看 12 13 a = A('sxc') 14 15 # print(a.__dict__) # 使用该方法时__dict__也被取消,会报错 16 # a.age = 18 # 并且不能添加新的属性 17 18 print(sys.getsizeof(a)) # 查看对象的内存占用,当没有__slots__方法时为56 19 print(sys.getsizeof(a)) # 查看对象的内存占用,有该方法时为48,内存确实被优化了
4.点. 括号[]语法的实现原理
(1)点.语法的实现原理(getattr,setattr,delattr)
getattr:用点访问属性时,如果属性不存在则执行
setattr:用点设置属性时执行
delattr:用del 对象.属性, 删除属性时执行
getattribute:该函数也是用来获取属性
在获取属性时,如果存在getattribute则先执行该函数,如果没有返回值,则再执行getattr,如果有返回值,则直接返回
1 # 点属性的实现原理getattr,setattr,delattr,getattribute 2 class A: 3 def __getattr__(self, item): # 该函数有返回值 4 print('执行了__getattr__') 5 return '__getattr__的返回值' # 该返回值只有属性没有返回值才会执行 6 7 def __setattr__(self, key, value): 8 print('执行了__setattr__') 9 self.__dict__[key] = value # 这就是点设置值语法的实现原理 10 11 def __delattr__(self, item): 12 print('执行了__delattr__') 13 self.__dict__.pop(item) # 这就是点删除值语法的实现原理 14 15 def __getattribute__(self, item): 16 # print('__getattribute__') 17 return super().__getattribute__(item) # 这就是点获取值语法的实现原理 18 19 a = A() 20 21 # 点语法内部是调用__dict__进行增删查改的操作 22 a.name = 'sxc' # 执行了setattr 23 # print(a.name) # 执行了getattr,并且有返回值 24 print(a.name) # 当有两个get方法时会先执行getattribute,没有返回值的情况下再执行getattr 25 del a.name # 执行了delattr 26 27 print(a.xxx) # 属性不存在的情况下执行getattr
点语法的实现原理是在上述方法里__dict__里加入新的值
(2)括号[]语法的实现原理(getitem,setitem,delitem)
getitem:当用中括号去获取属性时执行
setitem:当用中括号去设置属性时执行
delitem:当用中括号去删除属性时执行
1 # 括号语法的实现原理 2 class A: 3 def __getitem__(self, item): 4 print('__getitem__') 5 return self.__dict__[item] # 这就是括号获取值语法的实现原理 6 7 def __setitem__(self, key, value): 8 print('__setitem__') 9 self.__dict__[key] = value # 这就是括号设置值语法的实现原理 10 11 def __delitem__(self, key): 12 print('__delitem__') 13 self.__dict__.pop(key) # 这就是括号删除值语法的实现原理 14 15 16 a = A() 17 a['name'] = 'sxc' # 可以括号赋值 18 print(a['name']) # 可以括号取值 19 del a['name'] # 可以括号删值
可以使用点语法又可以使用括号语法
1 classes = {} 2 class Student(dict): 3 def __getattr__(self, item): 4 return self.get(item) 5 6 def __setattr__(self, key, value): 7 self[key] = value 8 9 def __delattr__(self, key): 10 del self[key] 11 12 s1 = Student() 13 s1['name'] = 'zzp' 14 print(s1.name) 15 16 s1.age = 28 17 print(s1['age']) 18 19 s1['classes'] = 'py10期' 20 print(s1.classes)
5.运算符重载
当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数,当我们需要自定义对象的比较规则时,就可以在子类中覆盖大于 等于 小于等一系列的方法
1 class Student: 2 def __init__(self,name,age,height,weight): 3 self.name = name 4 self.age = age 5 self.height = height 6 self.weight = weight 7 8 def __gt__(self, other): # 大于 9 return self.age > other.age 10 11 def __lt__(self, other): # 小于 12 return self.height < other.height 13 14 def __eq__(self, other): # 等于 15 return self.weight == other.weight 16 17 s1 = Student('sxc',18,175,60) 18 s2 = Student('zzj',20,174,60) 19 20 21 print(s1 > s2) # 比较年龄 22 print(s1 < s2) # 比较身高 23 print(s1 == s2) # 比较体重
作业2:创建一系列student对象存储到列表中, 然后编写排序算法,将对象按照年龄排序,排序算法不限
1 # 运算符重载 2 # 创建一系列student对象存储到列表中, 然后编写排序算法,将对象按照年龄排序,排序算法不限 3 class Student: 4 def __init__(self,name,age,height,weight): 5 self.name = name 6 self.age = age 7 self.height = height 8 self.weight = weight 9 10 def __gt__(self, other): # 大于 11 return self.age > other.age 12 13 def __lt__(self, other): # 小于 14 return self.age < other.age 15 16 def __eq__(self, other): # 等于 17 return self.weight == other.weight 18 19 s1 = Student('sxc',18,175,60) 20 s2 = Student('zzj',20,174,60) 21 s3 = Student('zzp',21,156,50) 22 s4 = Student('lzx',17,156,50) 23 s5 = Student('zkj',25,156,50) 24 25 # print(s1 > s2) # 比较年龄 26 # print(s1 < s2) # 比较身高 27 # print(s1 == s2) # 比较体重 28 29 30 all_student = [] 31 all_student.append(s1) 32 all_student.append(s2) 33 all_student.append(s3) 34 all_student.append(s4) 35 all_student.append(s5) 36 37 for i in range(len(all_student)): 38 for j in range(i+1,len(all_student)): 39 if all_student[i] > all_student[j]: 40 all_student[i],all_student[j] = all_student[j],all_student[i] 41 42 for i in all_student: 43 print(i.name)按照年龄排序
6.迭代器协议
迭代器指的是具有__iter__和__next__的对象
我们可以为对象增加这两个方法来让对象变成一个迭代器
1 # 迭代器 2 class MyIter: 3 def __init__(self,num): # num用来限制迭代次数 4 self.num = num 5 self.c = 0 6 7 def __iter__(self): 8 return self 9 10 def __next__(self): 11 if self.c < self.num: # 加上逻辑判断来限制迭代次数 12 self.c += 1 13 return '123' 14 else: 15 raise StopIteration 16 17 for i in MyIter(10): 18 print(i)
自定义编写一个range方法
1 # 实现一个自定义的range 2 class Myrange: 3 def __init__(self,start, end, step): 4 self.start = start # 开始值 5 self.end = end # 结束值 6 self.step = step # 步长 7 8 def __iter__(self): 9 return self 10 11 def __next__(self): 12 a = self.start # 使得self.start能正常递增 13 self.start += self.step 14 if a < self.end: 15 return a 16 else: 17 raise StopIteration 18 19 for i in Myrange(1,10,2): 20 print(i)
7.上下文管理
指的是一段话的意义,要参考当前的场景,即上下文
在python中,上下文可以理解为一个代码区间,例如with open打开的文件仅在这个上下文中有效
两个方法
enter:表示进入上下文
exit:表示退出上下文
当执行with语句时,会先执行enter,当代码执行完毕后执行exit,或者代码遇到异常会立即执行exit,并传入错误信息
包含错误的类型,错误的信息,错误的追踪信息
1 class Myopen: 2 def __init__(self,path): 3 self.path = path 4 5 def __enter__(self): 6 print('__enter__') 7 self.file = open(self.path) 8 return self # 需要返回自己 9 10 def __exit__(self, exc_type, exc_val, exc_tb): 11 print('__exit__') 12 self.file.close() 13 return True # 返回错误有没有处理 14 15 with Myopen('a.txt') as m: 16 print(m.file.read())
注意:
enter函数应该返回对象自己
exit函数可以有返回值,是一个bool类型,用于表示异常是否被处理,仅在上下文中出现异常有用,如果为True,则表示异常被处理了,False表示异常未被处理,程序将中断报错.
与del的区别:
上下文关联,管理的是一个代码范围,出了范围自动清理
del管理的是对象的生命周期,会在对象销毁时执行清理