面向对象之反射,元类
一.反射
1.什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能能力(自省)。
2.python中的反射
反射是所有面向对象编程语言都具备的功能
python中通过一下四个函数来实现反射
1 hasattr(object,name) #判断对象是否拥有某个属性 2 setattr(object,name,value) #为对象增加新的属性 3 getattr(object,name,default) #从对象中获取某个属性 4 delattr(object,name) #从对象中删除某个属性
3.为什么需要反射
一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要做出修改,或是增加新属性时,使用反射的方式可以不需要修改源代码
反射的另一个优势(重点):可插拔设计
不仅限于修改已有的属性,通过反省能够发现已存在的属性,只要你给我一个对象我就能检查其拥有的属性,从而使用这些属性,而不需要提前了解这些对象,这大大提高了程序的扩展性
案例1:
1 import plugins 2 def run(plugin): 3 while True: 4 cmd = input('请输入指令:').strip() 5 if cmd == 'exit': 6 break 7 if hasattr(plugin,cmd): 8 fun = getattr(plugin,cmd) 9 fun() 10 else: 11 print('该指令不受支持') 12 print(‘已退出’)
1 #plugins 2 3 class Wincmd: 4 def dir(self): 5 print('wincmd列出所有文件。。。') 6 def cd(self): 7 print('wincmd 切换目录。。。') 8 def delete(self): 9 print('wincmd 要不要删除目录。。。 ')
对案例1进行改进,使用动态导入模块
案例2:
1.首先配置settings
class_path = 'lib.plugins.Wincmd'
2.主体框架:
1 import importlib 2 from 面向对象 import settings 3 def run(plugin): 4 while True: 5 cmd = input('请输入指令:') 6 if cmd == 'exit': 7 break 8 if hasattr(plugin,cmd): 9 func = getattr(plugin,cmd) 10 func() 11 else: 12 print('该指令不受支持') 13 print('see you la la') 14 15 path,class_name = settings.class_path.rsplit('.',1) 16 mk = importlib.import_module(path) #找到模块(文件) 17 cls = getattr(mk,class_name) #通过反射,模块与类名,得到类 18 obj = cls() #实例化对象 19 run(obj) #调用函数 传入对象
二.元类
1.什么是元类?
在Python中,一切皆对象,类也是对象。
验证一下:
1 class Person: 2 pass 3 print(type(Person)) 4 #<class 'type'>
由此看来Person类属于type类,是由type类产生的对象。
因此可以推导出:
用于实例化产生类的类称之为元类,就是此时的type类
2.创建类的流程分析:
一个类有三个基本组成部分:
1.类的名字(字符类型)
2.类的父类们(是一个元组或列表)
3.类的名称空间(字典类型)
用另一种方式出创建类
cls_obj = type('dog',(),{}) #同样可以创建类
3.自定义元类,通过自定义原来创建类
创建类是由type完成的type中必然包含了创建了的具体代码,现在需要对这些代码进行修改,两种方式:
1.修改type源代码不可取
2.创建新的元类,使用自己的元类来创建类从而实现定制类
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过该继承type
来自定义元类,然后使用metaclass关键字参数为一个类指定元类
1 class MyType(type): 2 def __init__(self,clss_name,bases,dict): 3 super().__init__(clss_name,bases,dict) 4 5 class Person(metaclass = MyType): 6 pass
4.在自定义元类中通过覆盖type类中的__init__来控制定义类
下面有个需求,我们高度自定义一个类,需要控制类的名字必须以大驼峰的方式来书写
这是需要在自定义元类中覆盖type类中的__init__方法。
class MyType(type): def __init__(self,cls_name,bases,dict): super().__init__(cls_name,bases,dict) if not cls_name.istitle(): raise Exception('类名要大写的驼峰') class Pig(metaclass = MyType): pass
5.元类中call方法(当你想要控制对象的创建过程时)
当你调用类对象时会自动珍惜元类中的__call__方法,并将这个类本身作为第一个参数传入,以及右面的一堆参数,覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并返回其返回值。
案例:实现将对象的所有属性名成转为大写
class MyType(type): def __call__(self,*args,**kwargs): for k,v in kwargs.items(): kwargs[k] = v.upper() return super().__call__(*args,**kwargs) class Person(metaclass = MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person(name = 'jack',gender = 'man') print(p.name) print(p.gender)
注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
6.单例设计模式
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象
需求:只生成一个对象
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 self.obj = obj # 并存入类中 7 return obj 8 9 class Person(metaclass=Single): 10 pass 11 12 # 只会创建一个对象 13 Person() 14 Person() 15 #创建再多个对象都是同一个
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)