面向对象之高级篇 反射,元类

今日内容

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__)
View Code

反射的案例:

 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()
View Code

二 元类

元类简介:
  元类是什么,用于创建类的类,万物皆对象,类也是对象,

  对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是列一个类

  实例化产生的,默认情况下所有类的元类都是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
View Code

元类中的call方法:

  当你调用类对象时会自动珍惜元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数

  覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
  并返回其返回值

使用场景: 

  当你想要控制对象的创建过程时,就覆盖call方法

  当你想要控制类的创建过程时,就覆盖init方法

案例:实现将对象的所有属性名称转为大写

 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)
View Code

注意: 一旦覆盖了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)
View Code

总结:

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)
View Code

元类总结: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__)
View Code

三 单列模式

什么是单列模式: 

  设计模式?用于解决某种固定问题的套路
  例如: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()
View Code
原文地址:https://www.cnblogs.com/zahngyu/p/11273112.html