反射 reflect、元类 metaclass(new方法的补充)、单例设计模式

反射 reflect

含义:

自省,反射是指一个对象应该具备可以检测修改,增加自身属性的能力。

方式:

通过字符串操作属性

涉及的四个函数(hasattr,getattr,setattr,delattr)只是普通的内置函数,与print等没有区别。

 1 hasattr  getattr setattr delattr 
 2 
 3 p = Person("jack",18,"man")
 4 
 5 # if hasattr(p,"name"): # 1.判断某个对象是否存在某个属性
 6 #     print(getattr(p,"names",None)) # 2.从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值
 7 
 8 # 3.为对象添加新的属性
 9 setattr(p,"id","123")
10 print(p.id)
11 
12 # 4.从对象中删除属性
13 delattr(p,"id")
14 print(p.id)
View Code

使用场景:

反射其实就是对属性的增删改查,但是如果直接使用内置的dict来操作,语法繁琐,不好理解

另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法。

框架设计方式:

框架代码:

"""
反射被称为框架的基石,为什么
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
需求:要实现一个用于处理用户的终端指令的小框架
框架就是已经实现了最基础的构架,就是所有项目都一样的部分
"""

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

元类 metaclass

元类是什么,用于创建类的类

万物皆对象,类也是对象

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

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

 1 class Person:
 2     pass
 3 p = Person()
 4 
 5 print(type(p))
 6 print(type(Person))
 7 
 8 证明:Person类是通过type类实例化产生的 
 9 #结果:
10 <class '__main__.Person'>
11 <class 'type'>
View Code

学习元类的目的:

高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

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

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

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

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

代码实现覆盖元类的__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("jack","woman")
18 print(p.name)
19 print(p.gender)
20 #结果:
21 ['JACK', 'WOMAN']
22 {}
23 JACK
24 WOMAN
View Code

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

补充new方法

当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象

即:# return super().__new__(cls,*args,**kwargs)

 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更简单

单例设计模式

设计模式:用于解决某种固定问题的套路
例如: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/yangjiaoshou/p/11272405.html