面向对象之反射 元类
一:反射
【1】基础概念:
(1)定义:反射指的是一个对象 应该具备 修改 检测 增加属性的能力
本质:属性的增删改查
(2)使用场景:
(1)当我框架搭建出来 需要向框架内部添加细节的时候 但是不知道该细节中内对象有什么属性 类支持什么功能
(2)此时可以通过反射询问对象含有什么属性 类支持什么功能
(3)涉及的四个函数
(1)hasattr
作用:查看某对象是否含有某些属性
例如:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person('SR',18) print(hasattr(p,'name')) # True
(2)getattr
作用:通过该函数可以获取对象的属性
例如:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person('SR',18) if hasattr(p,'name'): print(getattr(p,'name',None)) # SR print(getattr(p, 'names', None)) # None
PS:
(1)如果对象属性不存在 会报错
(2)也可以设置默认值参数 属性不存在返回None
(3)setattr
作用:设置对象的属性
例如:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person('SR',18) setattr(p,'id_cart','123') print(p.id_cart) # 123
(4)delattr
作用:删除对象的属性
例如:
class Person: def __init__(self,name,age): self.name = name self.age = age p = Person('SR',18) delattr(p,'id_cart') print(p.id_cart) # 报错 上述属性已经被删除
PS:上述四个方法都是属于普通函数 没有双下划线 非类内值的方法
反射的案例:
例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def run(self): while True: cmd = input('请输入你要执行的命令>>:') # 如果输入exit退出 if cmd == 'exit': break # 判断类是否有该方法 if hasattr(self,cmd): # 如果有该方法 获取 func= getattr(self,cmd) # 通过获取的方法调用 func() else: print('命令不支持!') win = Wincmd() linux = Linuxcmd() # 将对象以参数形式传入 调用类中的方法以及属性 run(win) run(linux)
(2)动态导入:
作用:
(1)当我框架设计好的时候 开始使用对象以及类
(2)但是作为框架的设计者 并不能知道对方的类叫什么名字 对象叫什么名字
(3)因此我们为框架的使用者创建一个配置文件 框架使用者将类的信息传入配置文件中
(4)框架设计者在该配置文件中调用所需的类
例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def run(self): while True: cmd = input('请输入你要执行的命令>>:').strip() # 如果输入exit退出 if cmd == 'q': break # 判断类是否有该方法 elif hasattr(self,cmd): # 如果有该方法 获取 func= getattr(self,cmd) # 通过获取的方法调用 func() else: print('命令不支持!') import importlib import settings # 在框架设计者 通过配置文件获取 使用者的类路径 以及类名称 PATH = settings.CLASS_PATH # print(PATH) # lib.plugins.Wincmd # 从模块中分别取出路径 以及类名称 module_path,class_name = PATH.rsplit(".",1) # print(module_path) # lib.plugins # print(class_name) # Wincmd # 通过导入路径名称 拿到模块 path = importlib.import_module(module_path) # 通过模块调取内部的类 cls = path.Wincmd # 进行类的实例化 获取对象 obj = cls() run(obj) # 正在执行cd命令!
PS:
(1)框架使用者可以自己定义自己的名称 只需将类名称以及路径设置配置文件即可
(2)框架设计者无需管框架使用者类中的名称以及属性 其只要从配置文件调用即可
二:元类
【1】基础概念
(1)定义:是用来创建类的类
解释:既然万物皆对象 类也属于对象 类对象就是通过元类产生的
例如:
class Person: pass p = Person() print(type(p)) print(type(Person)) # <class 'type'>
PS:类都是有type类产生的
(2)自定义类:
(1)基本组成:
(1)类名
(2)基类
(3)名称空间
例如:
cls = type('Name',(),{}) print(cls) # <class '__main__.Name'>
(3)作用:高度的自定义自己需要的类
例如:
# 自定义一个元类 class My_class(type): # 调用元类初始化方法 def __init__(self,class_name,bases,dict): super().__init__(class_name,bases,dict) # 判断类名是否已大写开头 if not class_name.istitle(): raise Exception ('类名不会写吗!') # 指定其所对应的元类 class Person(metaclass=My_class): pass class person(metaclass=My_class): # Exception: 类名不会写吗! pass
PS:通过上述案例 自己定义一个类 要求类名必须为大写字母开头
基本思路:
(1)首先类也是对象 我们想在创建类的时候做限制
(2)既然想在创建的类的时候做限制 可以使用初始化方法 本身我们不能随便修改源代码 但是我们可以调用元类中的初始化方法
(3)子类通过覆盖元类中的初始化方法实现需求
(4)call方法的应用
作用:在对象被调用对象的时候 会执行该方法
基础案例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class My_class(type): def __init__(self,name,bases,dict): super().__init__(name,bases,dict) def __call__(self, *args,**kwargs): print(self) # <class '__main__.Dog'> print(*args) # 大黄 print(**kwargs) #{} # 返回对象 return super().__call__(*args,**kwargs) class Dog(metaclass=My_class): def __init__(self,name): self.name = name def __call__(self, *args, **kwargs): print('call run') # 执行该方法的时候 会调用元类中的call方法 d = Dog('大黄') # 在元类中没有返回对象的时候 print(d) # None 而不是一个对象 # 元类中返回对象 print(d) # <__main__.Dog object at 0x00000000027F8B70>
PS:
(1)当你调用类对象的时候 会自动调用元类中的call方法 并且将这个类本身作为第一个参数传给元类 以及后面的一堆参数
(2)但是在自定义元类中因为调用了type这个元类 会覆盖原有元类call方法 这样原来的call方法就不能实例化对象 必须调用super().__call__来完成对象的创建
call进阶版:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 将对象姓名变为大写 class My_class(type): # 调用call方法 可以操作对象 def __call__(self, *args, **kwargs): new_args = [] # 循环打印 创建新的 for i in args: new_args.append(i.upper()) # 将新的对象返回 return super().__call__(*new_args,**kwargs) class Person(metaclass=My_class): def __init__(self,name): self.name = name p = Person('jack') print(p.name) # JACK
PS:
(1)如果操作类调用init方法 因为类的创建是初始化过程 init用来初始化
(2)操作对象可以调用call方法 对象的产生就是调用call方法 可以通过call方法 影响对象
(5)__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)
PS:
(1)首先会执行元类中的new方法 拿到一个空对象 然后自动调用init来对这个类进行初始化操作
(2)如果你覆盖了该方法 必须保证new方法有返回值 且为该类对应的 对象
(6)元类的单例设计模式:
(1)设计模式:指的是解决某种问题的固定模式
(2)单例:一个类只产生一个对象
作用:为了节省空间 一个类全部对象属性都相同时候 可以使用单例
例如:
-
# 单例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