面向对象高级部分
1.什么是反射?
反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力
反射就是通过字符串操作属性
主要涉及以下四个函数:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person('jack',18) print(p.name,p.age) # jack 18 print(hasattr(p,'name')) # 判断某个对象的属性是否存在某个属性;此处name在p中,所以返回结果为True print(getattr(p,'name',None)) # 从对象中取出属性,第三个值为默认值,当属性不存在时返回默认值 setattr(p,'sex','man') # 添加新的属性 print(p.sex) # 会将sex这个属性添加到p当中 delattr(p,'age') # 将从对象中删除属性 print(p.age) # 打印结果不存在,会报错
2.使用场景:
反射其实就是对属性的增删查改,但是如果直接使用内置的dict来操作,语法繁琐,不好理解
另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法
框架设计方式:
反射被称为框架的基石,为什么 因为框架的设计者,不可能提前知道你的对象到底是怎么设计的 所以你提供给框架的对象 必须通过判断验证之后才能正常使用 判断验证就是反射要做的事情, 当然通过dict也是可以实现的, 其实这些方法也就是对dict的操作进行了封装
二、元类
1.元类是什么?
它是用于创建类的类,类也是一个对象
对象是通过类实例化产生的,既然类也是对象,那么类对象也是由另一个类实例化产生的
默认情况下所有累的元类都是type
2.验证:
class Person: pass p = Person() print(type(p)) # <class '__main__.Person'> print(type(Person)) # <class 'type'> Person类是通过type类实例化产生的
3.学习类的目的
高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写
类也是对象,也有自己的类,
我们的需求是创建类对象做一些限制
想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求
当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
只要继承了type,那么这个类就变成了一个元类 定义一个类 class MyType(type): def __init__(self,class_name,bases,dict): super().__init__(class_name,bases,dict) print(class_name,bases,dict) if not class_name.istitle(): raise Exception('类名错误') class pig(metaclass=MyType): pass class Duck(metaclass=MyType): pass
当调用类对象时会自动启动元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的call之后,这个类就无法产生对象,必须调用super().call来完成对象的创建 并返回其返回值
使用场景:
当想要控制对象的创建过程时,就覆盖call方法
当想要控制类的创建过程时,就覆盖init方法
例题:
实现将对象的所有属性名称转为大写
class MyType(type): def __call__(self, *args, **kwargs): new_args = [] for a in args: new_args.append(a.upper()) print(new_args) print(kwargs) return super().__call__(*new_args,**kwargs) class Person(metaclass=MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person(name="jack",gender="woman") print(p.name) print(p.gender)
注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
当要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作 注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象
6.单例设计模式
单例:指的是一个类产生一个对象
设计模式:是用于解决某种固定问题的套路
单例的作用:能够节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
# 单例n元类 class Single(type): def __call__(self, *args, **kwargs): if hasattr(self,"obj"): #判断是否存在已经有的对象 return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 没有则创建 print("new 了") self.obj = obj # 并存入类中 return obj class Student(metaclass=Single): def __init__(self,name): self.name = name class Person(metaclass=Single): pass # 只会创建一个对象 Person() Person()
三、冒泡排序的补充
将此列表从大到小进行排序
s = [2,1,3,5]
第一圈: [2,1,3,5] 第一次 得出2的位置 [2,1,3,5] 第二次 [2,3,1,5] 第三次 [2,3,5,1] 次数为 元素个数 - 1 - (圈数索引为0) 第二圈: [2,3,5,1] 第一次 [3,2,5,1] 第二次 [3,5,2,1] 次数为 元素个数 - 1 - (圈数索引为1) 第三圈: [3,5,2,1] 第一次 [5,3,2,1] 次数为 元素个数 - 1 - (圈数索引为2)
总结规律
圈数 是元素个数减一
次数 元素个数 - 1 - (圈数索引)
我们需要两层循环
一层控制圈数
一层控制次数
冒泡排序的模板
s = [2,1,3,5,100,24,12,12,1,2,1,1,4,32] for i in range(len(s)-1): for j in range(len(s)-1-i): # 如果前面的小于后面的则交换位置 if s[j] > s[j+1]: s[j],s[j+1] = s[j+1],s[j] print(s)