今日内容
1.反射
2.元类
3.单列模式
一 反射(反省)
什么是反省: 通过字符串来反射,映射到对象,类的属性上,英文中叫反省(自省)
面向对象中的反省,指的是一个对象必须具备,发现自身属性,以及修改自身属性的能力:
一个对象在设计初期,可能考虑不够周全,后期需要删除或者修改已经存在的属性,和增加属性
反省就是通过字符串啦操控对象属性
涉及到的有四种方法:
hasattr:判断是否存在某个属性
getattr:获取某个属性的值
setattr:新增或者修改某个属性
delattr:删除某个属性
注意:只要点能访问的属性,以上四个方法都能操作
案例:四个方法
1 class People: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def run(self): 7 print('%s is running'%self.name) 8 9 pe = People('zy',19)10 11 print(pe.__dict__)#{'name': 'zy', 'age': 19}12 13 print(pe.name) # pe.__dict__['name']14 15 pe.name = 'zhangyu' # pe.__dict__['name] = 'zhangyu'16 17 del pe.name # del pe.__dict__['name']18 19 四个方法20 查看是否在里面21 print(hasattr(pe,'name')) # 'name' in pe.__dict__22 23 获取值24 print(getattr(pe,'name')) # pe.__dict__['name']25 26 通过键获取值要是键不存在的话,会报错,但你也可以加上一个None让他不报错,打印None27 print(getattr(pe,'xxx',None)) # pe.__dict__['xxx']28 29 可以修改值30 setattr(pe,'name','zhangyu') # pe.__dict__['name']='zhnagyu'31 print(pe.name)32 print(pe.__dict__)# {'name': 'zhangyu', 'age': 19}33 34 删除35 把name这个键值对删除了36 delattr(pe,'name')37 print(pe.__dict__)
反射的案例:
1 class Ftp: 2 def get(self): 3 print('get') 4 5 def put(self): 6 print('put') 7 8 def login(self): 9 print('login')10 11 def run(self):12 while True:13 cmd = input('>>>').strip()14 if hasattr(self,cmd):15 method = getattr(self,cmd)16 method()17 else:18 print('输入的方法不存在')19 20 obj = Ftp()21 22 obj.run()
二 元类
元类简介: 元类是什么,用于创建类的类,万物皆对象,类也是对象,
对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是列一个类
实例化产生的,默认情况下所有类的元类都是type
案例:
class OldboyTeacher: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def score(self): print('%s is scoring'%self.name)t = OldboyTeacher('zy',19,'male')print(type(t))print(type(OldboyTeacher))对象t 是调用OldboyTeacher类得到的,如果说一切皆对象的话,那么OldboyTeacher也是一个对象,只要是对象,都是调用一个类实例化得到的,即OldboyTeacher = 元类,内置的元类是type
为什么要学习元类:
高度的自定义一个类,列如控制类的名字必须以大驼峰的方式来书写,
类也是对象,也有自己的类,我们的需求是创建类对象做一些限制
想到了初始化方法,我们只要找到类对象的类(元类)覆盖其中init方法就能实现需求
但是我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
案例:
1 """ 2 只要继承了type 那么这个类就变成了一个元类 3 """ 4 # 定义了一个元类 5 class MyType(type): 6 def __init__(self,clss_name,bases,dict): 7 super().__init__(clss_name,bases,dict) 8 print(clss_name,bases,dict) 9 if not clss_name.istitle():10 raise Exception("你丫的 类名不会写...")11 12 # 为pig类指定了元类为MyType13 class Pig(metaclass=MyType):14 pass15 16 class Duck(metaclass=MyType):17 pass
元类中的call方法:
当你调用类对象时会自动珍惜元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
并返回其返回值
使用场景:
当你想要控制对象的创建过程时,就覆盖call方法
1 class MyType(type): 2 def __call__(self, *args, **kwargs): 3 new_args = [] 4 for a in args: 5 new_args.append(a.upper()) 6 7 print(new_args) 8 print(kwargs) 9 return super().__call__(*new_args,**kwargs)10 11 12 class Person(metaclass=MyType):13 def __init__(self,name,gender):14 self.name = name15 self.gender = gender16 17 p = Person(name="jack",gender="woman")18 print(p.name)19 print(p.gender)
注意: 一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
元类中的new方法:
当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象
案例:
1 class Meta(type): 2 3 def __new__(cls, *args, **kwargs): 4 print(cls) # 元类自己 5 print(args) # 创建类需要的几个参数 类名,基类,名称空间 6 print(kwargs) #空的 7 print("new run") 8 # return super().__new__(cls,*args,**kwargs) 9 obj = type.__new__(cls,*args,**kwargs)10 return obj11 def __init__(self,a,b,c):12 super().__init__(a,b,c)13 print("init run")14 class A(metaclass=Meta):15 pass16 print(A)
总结:
new方法和init都可以实现控制类的创建过程,init更简单
三 单列模式
什么是单列模式:
设计模式?用于解决某种固定问题的套路
例如:MVCMTV等 单例:指的是一个类产生一个对象 为什么要使用单例:单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
元类实现案例:
1 # 单例n元类 2 class Single(type): 3 def __call__(self, *args, **kwargs): 4 if hasattr(self,"obj"): #判断是否存在已经有的对象 5 return getattr(self,"obj") # 有就返回 6 7 obj = super().__call__(*args,**kwargs) # 没有则创建 8 print("new 了") 9 self.obj = obj # 并存入类中10 return obj11 12 13 class Student(metaclass=Single):14 def __init__(self,name):15 self.name = name16 17 18 class Person(metaclass=Single):19 pass20 21 # 只会创建一个对象22 Person()23 Person()