面向对象反射、元类

一、什么是反射

       反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

二、为什么要反射

  一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要作出修改,或是增加新属性时,使用反射的方式可以不需要修改源代码

  反射的另一个优势:可插拔设计

三、反射的四个方法的使用

使用场景:反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,语法不好理解。另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法

hasattr、getattr、setattr、delattr

 1 class Person:
 2     def __init__(self,name,age,gender):
 3         self.name = name
 4         self.age = age
 5         self.gender = gender
 6 
 7 p = Person('zzj',18,'man')
 8 
 9 if hasattr(p,'name'): #判断某个对象是否存在某个属性
10     print(getattr(p,'name',None)) #从对象中取出属性,第三个值为默认值,当属性不存在时返回默认值
11 else:
12     print('不存在此方法')
View Code

四、反射的应用案例

反射被称为框架的基石,这是为什么呢,因为框架的设计者,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对、象,必须通过判断验证之后才能正常使用。判断验证就是反射要做的事情。当然通过__dlct__也是可以实现的,其实这些方法就是对__dict__的操作进行了封装

 1 import importlib
 2 import settings
 3 
 4 def run(plugins):
 5     while True:
 6         cmd = input('请输入指令: ').strip()
 7         if cmd == 'exit':
 8             break
 9         #因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
10         #判断对象是否具备处理这个指令的方法
11         if hasattr(plugins,cmd):
12             func = getattr(plugins,cmd)
13             func()
14         else:
15             print('该指令不受支持...')
16     print('goodbye')
17 #框架得根据配置文件拿到需要的类
18 path = settings.CLASS_PATH
19 #从配置中单独拿出来,模块路径和类名称
20 module_path,class_name = path.rsplit('.',1)
21 #拿到模块
22 mk = importlib.import_module(module_path)
23 #拿到类
24 cls = getattr(mk,class_name)
25 #实例化对象
26 obj = cls()
27 #调用框架
28 run(obj)
29 
30 plugins插件
31 
32 class WinCMD:
33 
34     def cd(self):
35         print("wincmd 切换目录....")
36 
37     def delete(self):
38         print("wincmd 要不要删库跑路?")
39 
40     def dir(self):
41         print("wincmd 列出所有文件....")
42 
43 class LinuxCMD:
44 
45     def cd(self):
46         print("Linuxcmd 切换目录....")
47 
48     def rm(self):
49         print("Linuxcmd 要不要删库跑路?")
50 
51     def ls(self):
52         print("Linuxcmd 列出所有文件....")
53 
54 settings配置文件
55 
56 CLASS_PATH = 'libs.plugins.WinCmd'
View Code

五、元类

一个类的三个基本组成部分:1.类的名字(字符类型) 2.类的父类们(是一个元组或列表) 3.类的名称空间字典类型

1.元类是什么?用于创建类的类

2.万物皆对象,类当然也是对象

3.对象时用过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的

4.默认情况下所有类的元类都是type

5.验证:

1 class Person:
2     pass
3 p = Person()
4 print(type(p)) #<class '__main__.Person'>
5 print(type(Person)) #<class 'type'>

6.学习元类的目的:1.高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

        2.类也是对象,也有自己的类

        3.我们的需求是创建类对象做一些限制

        4.想到了初始化方法,我们只要找到类对象的类(元素),覆盖其中init方法就能实现需求

        5.当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求

只要继承了type 那么这个类就变成了一个元类

 1 class MyType(type):
 2     def __init__(self,class_name,bases,dict):
 3         super().__init__(class_name,bases,dict)
 4         print(class_name,bases,dict)
 5         if not class_name.istitle():
 6             raise Exception('好好写类名!')
 7 class Pig(metaclass=MyType):
 8     pass
 9 
10 class duck(metaclass=MyType):
11     pass
View Code

7.元类中call方法

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

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

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

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

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

 1 class MyType(type):
 2     def __call__(self, *args, **kwargs):
 3         new_args = []
 4         for i in args:
 5             new_args.append(i.upper)
 6         return super().__call__(*new_args,**kwargs)
 7 
 8 class Person(metaclass=MyType):
 9     def __init__(self,name,gender):
10         self.name = name
11         self.gender = gender
12 
13 p = Person('zzj','man')
14 print(p.name())
15 print(p.gender())
View Code

  注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

案例2:要求创建对象时必须以关键字参数形式来传参

 1 class Mate(type):
 2     def __call__(self,*args,**kwargs):
 3         if args:
 4             raise Exception('请使用关键字形式参数来传参!')
 5         return super().__call__(*args,**kwargs)
 6 
 7 class A(metaclass=Mate):
 8     def __init__(self,name):
 9         self.name = name
10 
11 a = A(name='jack')
12 print(a.name)
13 
14 a = A('rose')
View Code

  

8.new方法(补充)

当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对个类进行初始化操作

注意:如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象

 1 class Meta(type):
 2     def __new__(cls, *args, **kwargs):
 3         print(cls)
 4         print(args)
 5         print(kwargs)
 6         print('new run')
 7         obj = type.__new__(cls,*args,**kwargs)
 8         return obj
 9 
10     def __init__(self,a,b,c):
11         super().__init__(a,b,c)
12         print('init run')
13 
14 class A(metaclass=Meta):
15     pass
16 
17 print(A)
View Code

  总结:new方法和init 都可以实心控制类的创建过程,init更简单

9.单例设计模式

单例:指的是一个类产生一个对象

为什么要使用单例:

单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

 1 class Single(type):
 2     def __call__(self, *args, **kwargs):
 3         if hasattr(self,'obj'): #判断是否存在已经有的对象
 4             return getattr(self,'obj') #有就返回
 5         obj = super().__call__(*args,**kwargs) #没有则创建
 6         print('new le')
 7         self.obj = obj #并存入类中
 8         return obj
 9 
10 class Student(metaclass=Single):
11     def __init__(self,name):
12         self.name = name
13     def say_hi(self):
14         return 'hello my name is %s'%self.name
15 
16 class Person(metaclass=Single):
17     def __init__(self,name):
18         self.name = name
19     def say_hi(self):
20         return 'hello my name is %s'%self.name
21 
22 #只会创建一个对象
23 stu = Student('zzj')
24 print(stu.say_hi())
25 
26 stu2 = Student('zkj')
27 print(stu.say_hi())
28 
29 stu3 = Student('zzp')
30 print(stu.say_hi())
31 
32 stu4 = Student('lzx')
33 print(stu.say_hi())
34 
35 p1 = Person('lzx')#创建新对象
36 print(p1.say_hi())
View Code
原文地址:https://www.cnblogs.com/spencerzhu/p/11272393.html