反射 元类 反射 refiect 元类 metaclass 单例设计模式
反射指的是一个对象应具备 可以检测,修改,增加自身属性的能力
反射就是通过字符串操作属性
涉及的四个内置函数
hasattr(对象名, 属性名) : 判断某个对象是否存在某个属性
getattr(对象名, 属性名, 默认值) : 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值
setattr(对象名, 属性名, 属性值) : 为对象增加新的属性和值
delattr(对象名, 属性名) : 删除对象的属性
class A: def __init__(self, name, age, gander): self.name = name self.age = age self.gander = gander def hai(self): print('hai') a = A('x', 20, 'man') # 判断某个对象是否存在某个属性 if hasattr(a, 'name'): # 从对象中取出属性,第三个值是默认值,当属性不存在时返回默认值 print(getattr(a, 'name', None)) # 为对象增加新的属性和值 setattr(a, 'id', 110) print(a.id) # 删除对象的属性 delattr(a, 'id') if hasattr(a, 'id'): print(a.id) else: print('没有 id') print(getattr(a, 'hai')) # >>> <bound method A.hai of <__main__.A object at 0x00000209A0549C18>> func = getattr(a, 'hai') func() # >>> hai
应用场景:
反射其实就是对一个对象属性的增删改查,也可以通过通过__dict__来操作,单语法繁琐
另外一个主要的问题是,如果对象不是我自己写,是另一方提供的,我就必须判断这个对象是否满足要求,也就是是否有我需要的属性和方法
框架设计方式:
'''
反射被称为框架的基石
框架的设计值,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象必须通过判断验证之后才能正常使用,判断验证就是反射要做的事情
'''
'''插件部分''' class WinCMD: def cd(self): print("wincmd 切换目录....") def delete(self): print("wincmd 要不要删库跑路?") def dir(self): print("wincmd 列出所有文件....") class LinuxCMD: def cd(self): print("Linuxcmd 切换目录....") def rm(self): print("Linuxcmd 要不要删库跑路?") def ls(self): print("Linuxcmd 列出所有文件....") '''配置文件''' # 为框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,框架自己去加载小的模块 CLASS_PATH = 'libs.plugins.LinuxCMD' '''框架部分''' from frame import settings import importlib def run(obj): while True: cmd = input('请输入指令:') if cmd == 'exit': break # 因为无法确定框架使用者是否传入正确的对象,所以需要使用反射来检测 # 判断对象是否具有处理这个指令的方法 if hasattr(obj, cmd): # 取出该方法 func = getattr(obj, cmd) # 执行该方法 func() else: print('该指令不支持') print('退出') # 框架得根据配置文件拿到需要的类 path = settings.CLASS_PATH # 从配置文件中单独拿出模块和类名 module_path, class_name = path.rsplit('.',1) # 拿到模块 mk = importlib.import_module(module_path) # 取出模块中的类 cls = getattr(mk, class_name) # 实例化对象 obj = cls() # 调用框架 run(obj) # 如此一来 框架就与现实代码彻底解耦了
元类 metaclass
type 就是所有类的元类
元类是用于创建类的类
万物皆对象,类也是对象
对象时通过类实例化产生的,所以类对象必然也是由另一个类实例化产生的
class A: pass a = A() print(type(a)) # <class '__main__.A'> print(type(A)) # <class 'type'> # A类是通过type类实例化产生的
学习元类的目的:
高度的自定一个类,例如控制类的名字必须以大驼峰的方式来书写
类也是对象,也有自己的类
class MyType(type): # 覆盖元类中的__init__方法 def __init__(self, cls_name, bases, dict): # 调用元类中的__init__方法,使用其初始化功能 super().__init__(cls_name, bases, dict) # 添加判断条件 if not cls_name.istitle(): raise Exception('首字母需要大写') class A(metaclass=MyType): pass print(type(A))
元类中的call方法
当你调用类对象时会自动执行原类中__call__方法,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的__call__之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并返回其返回值
使用场景:
当你想要控制对象创建过程时,就覆盖call方法
当你想要控制类的创建过程时,就覆盖init方法
案例:
实现将对象的所有属性名称大写: lass MyType(type): def __call__(self, *args, **kwargs): new_args = [] for a in args: new_args.append(a.upper()) print(new_args) print(kwargs) # 注意: 一旦覆盖了__call__ 必须调用父类的call方法来产生对象并返回这个对象 return super().__call__(*new_args,**kwargs) class Person(metaclass=MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person(name="jack",gender="woman") print(p.name) print(p.gender)
new方法
当你要创建对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:如果你覆盖了该方法则必须保证__new__方法必须有返回值,且必须是对应的类对象
class Meta(type): def __new__(cls, *args, **kwargs): print(cls) # 元类自己 print(args) # 创建类需要的几个参数 类名,基类,名称空间 print(kwargs) #空的 print("new run") # return super().__new__(cls,*args,**kwargs) obj = type.__new__(cls,*args,**kwargs) return obj def __init__(self,a,b,c): super().__init__(a,b,c) print("init run") class A(metaclass=Meta): pass print(A)
__new__方法和__init__都可以实现控制类的创建过程,__init__更简单
单例设计模式
用于解决某种问题的固定思路
例如:MVC MTV等
单例:指的是一个趔产生一个对象
目的:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必须要创建多个对象
# 单例n元类 class Single(type): def __call__(self, *args, **kwargs): if hasattr(self,"obj"): #判断是否存在已经有的对象 return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 没有则创建 print("new 了") self.obj = obj # 并存入类中 return obj class Student(metaclass=Single): def __init__(self,name): self.name = name class Person(metaclass=Single): pass # 只会创建一个对象 Person() Person()