今日内容
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让他不报错,打印None 27 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类指定了元类为MyType 13 class Pig(metaclass=MyType): 14 pass 15 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 = name 15 self.gender = gender 16 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 obj 11 def __init__(self,a,b,c): 12 super().__init__(a,b,c) 13 print("init run") 14 class A(metaclass=Meta): 15 pass 16 print(A)
总结:
new方法和init都可以实现控制类的创建过程,init更简单
元类总结:owen版
1 # 元类:所有自定义的类本身也是对象,是元类的对象,所有自定义的类本质上是由元类实例化出来了 2 Student = type('Student', (object, ), namespace) 3 4 class MyMeta(type): 5 # 在class Student时调用:Student类的创建 => 来控制类的创建 6 7 # 自定义元类,重写init方法的目的: 8 # 1.该方法是从type中继承来的,所以参数同type的init 9 # 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type 10 # 3.在交给type最终完成工作之前,可以对类的创建加以限制 ***** 11 def __init__(cls, class_name, bases, namespace): 12 # 目的:对class_name | bases | namespace加以限制 ********************** 13 super().__init__(class_name, bases, namespace) 14 15 # 在Student()时调用:Student类的对象的创建 => 来控制对象的创建 16 17 # 自定义元类,重写call方法的目的: 18 # 1.被该元类控制的类生成对象,会调用元类的call方法 19 # 2.在call中的返回值就是创建的对象 20 # 3.在call中 21 # -- 通过object开辟空间产生对象 22 # -- 用被控制的类回调到自己的init方法完成名称空间的赋值 23 # -- 将修饰好的对象反馈给外界 24 def __call__(cls, *args, **kwargs): 25 # 目的:创建对象,就可以对对象加以限制 ********************** 26 obj = object.__new__(cls) # 通过object为哪个类开辟空间 27 cls.__init__(obj, *args, **kwargs) # 调回当前被控制的类自身的init方法,完成名称空间的赋值 28 return obj 29 30 # 问题: 31 # 1.继承是想获得父级的属性和方法,元类是要将类的创建于对象的创建加以控制 32 # 2.类的创建由元类的__init__方法控制 33 # -- 元类(class_name, bases, namespase) => 元类.__init__来完成实例化 34 # 3.类的对象的创建由元类的__call__方法控制 35 # -- 对象产生是需要开辟空间,在__call__中用object.__new__()来完成的 36 class Student(object, metaclass=MyMeta): 37 pass 38 39 # class Student: <=> type(class_name, bases, namespace)
元类总结:egon版
1 ''' 2 元类介绍 3 什么是元类: 4 源自一句话:在Python中,一切皆对象,而对象都是由类实例化得到的 5 6 ''' 7 8 # class OldboyTeacher: 9 # def __init__(self,name,age,sex): 10 # self.name = name 11 # self.age = age 12 # self.sex = sex 13 # 14 # def score(self): 15 # print('%s is scoring'%self.name) 16 # 17 # t = OldboyTeacher('zy',19,'male') 18 # print(type(t)) 19 # print(type(OldboyTeacher)) 20 ''' 21 对象t 是调用OldboyTeacher类得到的,如果说一切皆对象的话, 22 那么OldboyTeacher也是一个对象,只要是对象,都是调用一个类 23 实例化得到的,即OldboyTeacher = 元类,内置的元类是type 24 25 关系 26 1. 调用元类----->自定义的类 27 2. 调用自定义的类---->自定义的对象 28 ''' 29 30 31 32 ''' 33 class关键字创建自定义类的底层的工作原理,分为四步 34 1.先拿到类名:'OldboyTeacher' 35 2.再拿到类的基类们:(object) 36 3.然后拿到类的名称空间(执行类体代码,将产生的名字放到类的名称空间,也就是一个字典里,补充一个exec) 37 4. 调用元类实例化得到自定义的类: OldboyTeacher=type('OldboyTeacher',(object,),{...}) 38 ''' 39 # class OldboyTeacher:# OldboyTeacher=type(...) 40 # school = 'Oldboy' 41 # def __init__(self,name,age,sex): 42 # self.name = name 43 # self.age = age 44 # self.sex = sex 45 # 46 # def score(self): 47 # print('%s is scoring'% self.name) 48 ''' 49 自定义类的三个关键组成部分: 50 1. 类名 51 2. 类的基类们 52 3. 类的名称空间 53 ''' 54 55 56 ''' 57 案例: 58 如何不依赖class关键字创建一个自定义类 59 ''' 60 # # 1. 拿到类名 61 # class_name='OldboyTeacher' 62 # #2. 拿到类的基类们:(object,) 63 # class_bases=(object,) 64 # #3. 拿到类的名称空间 65 # class_dic={} 66 # class_body=""" 67 # school = 'Oldboy' 68 # 69 # def __init__(self,name,age,sex): 70 # self.name=name 71 # self.age=age 72 # self.sex=sex 73 # 74 # def score(self): 75 # print('%s is scoring' %self.name) 76 # """ 77 # exec(class_body,{},class_dic) 78 # print(class_dic) 79 # #4. 调用type得到自定义的类 80 # OldboyTeacher=type(class_name,class_bases,class_dic) 81 # 82 # print(OldboyTeacher) 83 # # print(OldboyTeacher.school) 84 # # print(OldboyTeacher.score) 85 # 86 # tea1=OldboyTeacher('egon',18,'male') 87 # print(tea1.__dict__) 88 # 89 90 91 92 ''' 93 自定义元类来控制类的产生 94 模板 95 ''' 96 # class Mymeta(type):# 单反继承了type的类才能称之为自定义的元类,否则就是一个普通的类 97 # def __init__(self,class_name,class_bases,class_dic): 98 # print(self) 99 # print(class_name) 100 # print(class_bases) 101 # print(class_dic) 102 # 103 # class OldboyTeacher(object,metaclass=Mymeta):#OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...}) 104 # school = 'Oldboy' 105 # 106 # def __init__(self,name,age,sex): 107 # self.name = name 108 # self.age = age 109 # self.sex = sex 110 # 111 # def score(self): 112 # print('%s is scoring'%self.name) 113 114 115 ''' 116 控制类的产生 117 118 1. 类名必须用驼峰法 119 2. 类体必须有文档注释,且文档注释不能为空 120 ''' 121 # class Mymeta(type): # 单反继承了type的类才能称之为自定义的元类,否则就是一个普通的类 122 # def __init__(self, class_name, class_bases, class_dic): 123 # if class_name.islower(): 124 # raise TypeError ('类名必须使用驼峰体') 125 # 126 # doc = class_dic.get('__doc__') 127 # if doc is None or len(doc) == 0 or len(doc.strip(' ')) == 0: 128 # raise TypeError ('类体中必须有文档注释,且文档注释不能为空') 129 # 130 # class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...}) 131 # school = 'Oldboy' 132 # def __init__(self, name, age, sex): 133 # self.name = name 134 # self.age = age 135 # self.sex = sex 136 # 137 # def score(self): 138 # print('%s is scoring' % self.name) 139 # print(OldboyTeacher.__dict__) 140 141 142 143 ''' 144 自定义元类来控制类调用的过程 145 #总结:对象之所以可以调用,是因为对象的类中有一个函数__call__ 146 #推导:如果一切皆对象,那么OldboyTeacher也是一个对象,该对象之所可以调用,肯定是这个对象的类中也定义了一个函数__call__ 147 ''' 148 149 # 实例化OldboyTeacher,或者说调用OldboyTeacher类 150 #1.先产生一个空对象 151 #2.执行__init__方法,完成对象的初始化属性操作 152 #3.返回初始化好的那个对象 153 #推导:调用OldboyTeacher(....)就是在调用OldboyTeacher的类中的__call__,那么在__call__中就腰做以上三件事 154 155 # 自定义元类来控制类的调用(即类的实例化过程) 156 157 class Mymeta(type): # 单反继承了type的类才能称之为自定义的元类,否则就是一个普通的类 158 def __call__(self,*args,**kwargs):#self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={} 159 # 1.先产生一个空对象 160 tea_obj = self.__new__(self)# tea_obj是OldboyTeacher这个类的对象 161 # 2. 执行__init__方法,完成对象的初始属性操作 162 self.__init__(tea_obj,*args,**kwargs) 163 # 变为私有的 164 tea_obj.__dict__ = {('_%s__%s' % (self.__name__, k)): v for k, v in tea_obj.__dict__.items()} 165 # 3. 返回初始化好的那个对象 166 return tea_obj 167 168 class OldboyTeacher(object, metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...}) 169 school = 'Oldboy' 170 def __init__(self, name, age, sex): 171 self.name = name 172 self.age = age 173 self.sex = sex 174 175 def score(self): 176 print('%s is scoring' % self.name) 177 178 tea1 = OldboyTeacher('egon',18,'male')# 会触发OldboyTeacher的类,即(元类)中的__call__函数 179 print(tea1.__dict__)
三 单列模式
什么是单列模式:
设计模式?用于解决某种固定问题的套路
例如: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 obj 11 12 13 class Student(metaclass=Single): 14 def __init__(self,name): 15 self.name = name 16 17 18 class Person(metaclass=Single): 19 pass 20 21 # 只会创建一个对象 22 Person() 23 Person()