反射 reflect:
反射 其实不是我们字面意思的折射,而是反省,自检的意思
反射指的是一个对象应具备,可以检测,修改,增加自身属性的能力
反射就是通过字符串操作属性
涉及了四个函数,这四个函数其实就是普通的内置函数,没有双下划线
和print() len()一样没有什么其他的特殊区别
这四个函数分别是:
hasattr: 判断某个对象是否存在某种属性
getattr: 从对象中获取属性,第三个值为默认值,当属性不存在时返回默认值
setattr: 往对象里面添加新的属性
delattr: 从对象里面删除属性
实例: class Person: def __init__(self, name, age): self.name = name self.age = age p = Person("哨兵", 18) # print(p.name) if hasattr(p,"name"): print("对象P里面存在'name'这个属性") # >>>对象P里面存在'name'这个属性 print(getattr(p,'age',None)) # 执行结果>>>哨兵 最后的返回值None属性存在时不写也可以 print(getattr(p, 'asd',None)) # 当属性不存在时,会返回None,如果这个时候没有写None会报错 setattr(p,"id",23456) # setattr 为对象添加新的属性 setattr(p,"age",23456) # 当属性存在时,会覆盖老的值 print(p.age) delattr(p,"age") # 从对象里面删除指定的值 print(p.age) #>>> AttributeError: 'Person' object has no attribute 'age'
使用场景:
反射其实就是对属性的增删改查,但是如果直接使用内置的dict来操作,语法繁琐,而且不便于阅读理解
另外最重要的问题是:
如果对象不是我们自己写的是另外一方提供的,那么我们就要必须判断这个对象是否可以满足我们的需求
也就是说是否使我们所需要的属性和方法
框架设计方式:
框架代码:
#反射的四个函数实例: class Person: def __init__(self, name, age): self.name = name self.age = age p = Person("哨兵", 18) # print(p.name) if hasattr(p,"name"): print("对象P里面存在'name'这个属性") # >>>对象P里面存在'name'这个属性 print(getattr(p,'age',None)) # 执行结果>>>哨兵 最后的返回值None属性存在时不写也可以 print(getattr(p, 'asd',None)) # 当属性不存在时,会返回None,如果这个时候没有写None会报错 setattr(p,"id",23456) # setattr 为对象添加新的属性 setattr(p,"age",23456) # 当属性存在时,会覆盖老的值 print(p.age) delattr(p,"age") # 从对象里面删除指定的值 print(p.age) #>>> AttributeError: 'Person' object has no attribute 'age' ''' """
反射被称之为框架基石,因为框架的设计者不可能提前知道你的对象到底是怎么设计的
所以你提供框架的对象,必须通过判断验证之后才可以正常使用,而3判断和验证就是反射需要做的事情
当然也可以使用__dict__来实现,期是这种方法就是把__dict__的操作进行了封装
实例说明:
需求: 要实现一个用于处理用户终端指令的模拟小框架
首先分为框架部分
插件部分
配置文件部分
#框架部分: import importlib import settings def run(plugin): while True: cmd = input("请输入命令").strip() if cmd == "exit": break if hasattr(plugin,cmd): # 判断对象是否具有处理这个指令的方法 # 如果有 func = getattr(plugin,cmd) func() else: print("该指令不存在") # print("see you la la ") return "hahaha" path = settings.CLASS_PATH # 从配置文件setings中拿到模块的路径和类的名字 module_path, class_name = path.rsplit(".",1) #拿到模块 lk = importlib.import_module(module_path) # 拿到类 cls = getattr(lk,class_name) res = cls() run(res) 配置文件部分settings文件 ###这个文件作为框架的配置文件### CLASS_PATH = "lib.plugins.LinuxCMD" 插件部分在LIB文件下的plugins 属于插件部分 class WinCMD: def cd(self): print("wincod 切换目录") def delete(self): print("wincod 删除文件") def dir(self): print("wincod 列出所有文件") class LinuxCMD: def cd(self): print("Linuxcmd 切换目录....") def rm(self): print("Linuxcmd 要不要删库跑路?") def ls(self): print("Linuxcmd 列出所有文件....")
元类:
元类就是用于创建类的类,也就是原始类,大多数情况下都是type
万物皆对象,类也就是对象了,那么对象是有类实例化产生的,那么类也就是
另一个类实例化产生的,所以说默认情况下所有的类元类就是TYPE
#元类验证实例
class A:
pass
a = A()
print(type(a)) # a 的元类是 <class '__main__.A'>
print(type(A)) # A 的元类是 <class 'type'>
学习元类的目的:
高度自定义一个类,类也是对象,也有自己的类也就是元类
我们可以对类创建对象多一些限制,那么我们就可以在初始化上面做一些手脚
只要我们找到类对象的元类,覆盖其中的__init__方法就可以实现
因为我们不能修改源代码,所以我们应该是用继承元类的方式来编写自己,同时
覆盖__init__来实现
元类的实例
''' 直接调用元类type类来产生对象 cls = type("泰迪",(),{}) print(cls) 一个类的三个基本组成部分 1.类的名字(字符串类型的) 2.父类的名字(列表或者元祖组成) 3.类的名称空间(字典类型) '''
class MyTypt(type): def __init__(self,class_name,bases, dict ): super().__init__(class_name,bases, dict) # 元类的__init__方法 print(class_name,bases, dict) if not class_name.istitle(): # 限制类的首字母大写 raise Exception("类名请单词首字母大写") class Dig(metaclass=MyTypt): pass class Duck(metaclass=MyTypt): pass class ds(metaclass=MyTypt): pass < raise Exception("类名请单词首字母大写") Exception: 类名请单词首字母大写 >
元类中的__call__方法
当你调用类对象时会自动制行元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数
覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建
并返回其返回值
首先使用场景:
想要控制对象的创造过程时需要使用__call__方法
想要控制类创造过程中时候,就要覆盖__init__方法
实例: class Da(type): def __call__(self, *args, **kwargs): new_args = [] for i in args: new_args.append(i.upper()) print(new_args) print(kwargs) return super().__call__(*new_args,**kwargs) class Ap(metaclass=Da): def __init__(self, name, gender): self.name = name self.gender = gender # def __call__(self, *args, **kwargs): a = Ap("jack","n") print(a.name) print(a.gender) 实例2: class Mate(type): def __call__(self, *args, **kwargs): if args: raise Exception("不好意思不能使用未知参数") return super().__call__(*args, **kwargs) class A(metaclass=Mate): def __init__(self,name ): self.name = name a = A(name = "jack") print(a.name) 注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
new的方法
当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象
class Meta(type): def __new__(cls, *args, **kwargs): print(cls) # 元类自己 print(*args) # 创建需要的几个参数,类名,基类,名称空间 print(**kwargs) # 空的 print("new_run") res = type.__new__(cls,*args,**kwargs) return res def __init__(self,a,b,c): super().__init__(a,b,c) print("init_run") class A(metaclass=Meta): pass print(A)
总结new方法和init 都可以实现控制类的创建过程,init更简单
单例涉及模式:
设计模式,用于解决问题的方式的某种套路
单例:指的是一个类产生一个对象
为什要使用单例:
单例是为了节省资源,当一个类的所有对象属性全部相同时,测没有必要创建多对象
实例: 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()