面向对象-反射和元类
一、反射(reflect)
反射指的是一个对象应该具备可以检测,修改,增加自身属性的能力(反射其实就是对属性的增删改查),
通过字符串操作属性
涉及的四个函数,就是普通的内置函数
2.判断属性
hasattr 是否存在
getattr 取出属性
setattr 添加属性
delattr 删除属性
hasattr getattr setattr delattr p = Person("jack",18,"man") # if hasattr(p,"name"): # 1.判断某个对象是否存在某个属性 # print(getattr(p,"names",None)) # 2.从对象中取出属性,第三个值位默认值 当属性不存在是返回默认值 # 3.为对象添加新的属性 setattr(p,"id","123") print(p.id) # 4.从对象中删除属性 delattr(p,"id") print(p.id)
3.使用场景:
框架设计方式
框架代码
""" 反射被称为框架的基石,为什么 因为框架的设计者,不可能提前知道你的对象到底是怎么设计的 所以你提供给框架的对象 必须通过判断验证之后才能正常使用 判断验证就是反射要做的事情, 当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装 需求:要实现一个用于处理用户的终端指令的小框架 框架就是已经实现了最基础的构架,就是所有项目都一样的部分 """ import plugins # 框架已经实现的部分 def run(plugin): while True: cmd = input("请输入指令:") if cmd == "exit": break # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测 # 判断对象是否具备处理这个指令的方法 if hasattr(plugin,cmd): # 取出对应方法方法 func = getattr(plugin,cmd) func() # 执行方法处理指令 else: print("该指令不受支持...") print("see you la la!") # 创建一个插件对象 调用框架来使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定义对象来完成 linux = plugins.LinuxCMD() run(linux)
插件部分
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 列出所有文件....")
上述框架代码中 写死了必须使用某个类,这是不合理的,因为无法提前知道对方的类在什么地方 以及类叫什么
然后框架自己去加载需要的模块
import importlib import settings # 框架已经实现的部分 def run(plugin): while True: cmd = input("请输入指令:") if cmd == "exit": break # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测 # 判断对象是否具备处理这个指令的方法 if hasattr(plugin,cmd): # 取出对应方法方法 func = getattr(plugin,cmd) func() # 执行方法处理指令 else: print("该指令不受支持...") print("see you la la!") # 创建一个插件对象 调用框架来使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定义对象来完成 # 框架 得根据配置文件拿到需要的类 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
class Person: pass p = Person() print(type(p)) print(type(Person)) Person类是通过type类实例化产生的
1、元类的目的:对创建类对象做一些限制,通过初始化方法,覆盖其中init方法就能实现需求
代码:
""" 只要继承了type 那么这个类就变成了一个元类 """ # 定义了一个元类 class MyType(type): def __init__(self,clss_name,bases,dict): super().__init__(clss_name,bases,dict) print(clss_name,bases,dict) if not clss_name.istitle(): raise Exception("你丫的 类名不会写...") # 为pig类指定了元类为MyType class Pig(metaclass=MyType): pass class Duck(metaclass=MyType): pass
2、元类中call方法
当你调用类对象时会自动执行元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
并返回其返回值
场景:
当你想要控制类的创建过程时,就覆盖init方法
class MyType(type): def __call__(self, *args, **kwargs): new_args = [] for a in args: new_args.append(a.upper()) print(new_args) print(kwargs) return super().__call__(*new_args,**kwargs) class Person(metaclass=MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person("jack",gender="woman") print(p.name) print(p.gender)
注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
3、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更简单
4、单例设计模式
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省 资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
# 单例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()