1.反射
反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力
反射就是通过字符串操作属性
涉及四个函数,这四个函数就是普通的内置函数,没有双下划线,与print等在使用方法上没有区别
hasattr,getattr,setattr,delattr(他们的具体使用方法和作用见下列代码)
1 class Person: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 p = Person('sxc',18) 7 8 if hasattr(p,'name'): # 判断某个对象是否存在某个属性 9 print(getattr(p,'name')) # 把这个值当做返回值返回出来 10 11 print(getattr(p,'gender',None)) # 如果没有这个值后面还可以赋予他一个默认值 12 13 setattr(p,'id',177) # 可以为该对象赋予新的属性 14 print(p.id) 15 16 delattr(p,'id') # 也可以删除的对象的属性 17 print(p.id)
使用场景:
反射其实就是对属性的增删查改,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解
如果是另一方提供的,无法判断是否是我需要的属性和方法
简单的框架设计
框架就是已经实现了最基础的构架,就是所有项目都一样的部分已经完成了。反射被称为框架的基石,框架的设计者不可能提前知道使用者的对象到底是怎么设计的,所以使用者提供给框架的对象必须通过判断验证之后才能正常使用,判断验证就是反射要做的事
需求:实现一个用于处理用户的终端指令的小框架
1 # 框架部分 2 def run(plugin): 3 while True: 4 cmd = input('请输入您的指令>>>:').strip() 5 if cmd == 'exit': 6 break 7 # 无法判断传入的参数,需要加入反射加以判断 8 if hasattr(plugin,cmd): # 取出对应的指令 9 func = getattr(plugin,cmd) 10 func() # 执行指令处理 11 else: 12 print('请输入正确的指令') 13 print('退出程序') 14 15 # 从系统配置文件中截取模块路径和类名 16 module_path, class_name = settings.CLASS_PATH.rsplit('.',1) 17 # 拿到模块 18 mk = importlib.import_module(module_path) 19 # 拿到类 20 cls = getattr(mk,class_name) 21 # 实例化对象 22 obj = cls() 23 # 调用框架 24 run(obj)
# 配置文件路径 CLASS_PATH = 'lib.plugin.WinCmd'
1 class WinCmd: 2 def cd(self): 3 print('进入文件') 4 5 def delate(self): 6 print('删库跑路') 7 8 def dir(self): 9 print('文件列表') 10 11 12 class LinuxCmd: 13 def cd(self): 14 print('进入文件') 15 16 def rm(self): 17 print('删库跑路') 18 19 def ls(self): 20 print('文件列表')
2.元类 metaclass
用于创建类的类就叫做元类
在python中万物皆对象,类也是对象
对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是由另一个类实例化产生的
默认情况下所有类的元类都是type
元类的验证:
1 # 元类的验证 2 class Person: 3 pass 4 5 p = Person() 6 7 # type方法可以查看对象的类型 8 print(type(p)) 9 print(type(Person))
学习元类的目的:
可以高度自定义一个类,具体到可以控制类的名字必须以固定的格式书写
我们可以通过初始化方法,只要继承元类并覆盖其中的__init__方法就能实现需求
案例,通过元类限制类的名字必须以大驼峰的方式命名
1 # 继承元类可以高度定义一个子类 2 class MyType(type): 3 def __init__(self,cls,bases,dict): 4 super().__init__(cls,bases,dict) 5 if not cls.istitle(): 6 raise Exception('请小写') 7 # 生成两个子类 8 class Pig(metaclass= MyType): 9 pass 10 11 class duck(metaclass= MyType): 12 pass
Pig类可以通过,duck类并未大写,报错
元类中call方法
当调用类对象时会自动执行元类中的__call__方法,并将这个类本身作为第一个参数传入,以及后面的参数
覆盖元类中的call之后,就无法产生对象.必须调用super().__call__来完成对象的创建,并返回其返回值
1 # __call__是用来实例化对象返回值的 2 class MyType(type): 3 def __call__(self, *args, **kwargs): 4 print('元类call') 5 print(args) 6 print(kwargs) 7 8 class Person(metaclass=MyType): 9 def __init__(self,name): 10 self.name = name 11 12 p = Person('sxc',age = 18) 13 print(p)
当我们覆盖了元类的__call__方法,他的返回值就变成了None,说明__call__是用来实例化对象传值的
案例1:把子类中的所有属性都改为大写
1 # 把子类的所有属性都改成大写 2 class MyType(type): 3 def __call__(self, *args, **kwargs): 4 new_args = [] # 定义一个新列表 5 for a in args: # 循环输出旧列表中的内容 6 new_args.append(a.upper()) # 添加到新列表中 7 return super().__call__(*new_args,**kwargs) 8 9 10 class Person(metaclass=MyType): 11 def __init__(self,name): 12 self.name = name 13 14 15 p = Person('sxc') 16 p1 = Person('aabbcceeff') 17 print(p.name) 18 print(p1.name)
案例2:要求创建对象时只能关键字传参
1 # 要求:只允许关键字传参 2 class MyType(type): 3 def __call__(self, *args, **kwargs): # 覆盖元类的__call__方法 4 if args: 5 raise Exception('不允许直接传参') # 当直接传参时报错 6 return super().__call__(*args,**kwargs) 7 8 class Person(metaclass= MyType): 9 def __init__(self,name,age): 10 self.name = name 11 self.age = age 12 13 p1 = Person(name = 'sxc',age = 18) 14 print(p1.name,p1.age) 15 p = Person('sxc',18)
注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
__init__方法和__call__方法的使用场景:
当想要控制对象的创建过程时,就覆盖call方法
当想要控制类的创建过程时,就覆盖init方法
补充new方法
当创建类对象时,会首先窒息感元类中的__new__方法,拿到一个空对象,然后自动调用__init__来初始化
1 # new方法 2 class Mytype(type): 3 def __new__(cls, *args, **kwargs): 4 print(cls) # 元类自己 5 print(args) # 创建类需要的几个参数 类名,基类,名称空间 6 print(kwargs) # 空的值 7 print('new run') 8 return super().__new__(cls, *args, **kwargs) 9 10 def __init__(self,a,b,c): 11 super().__init__(a,b,c) 12 print('init run') 13 14 class A(metaclass= Mytype): # new方法和call方法在类生成时自动执行 15 pass 16 17 print(A) # 类对象本身
注意:当我们覆盖了__call__方法必须保证new方法有返回值并且是对应的类对象
单例设计模式
单例是为了节省资源,当一个类的所有对象属性全部相同时,没有必要创建多个对象
单例实现
1 # 单例的实现 2 class Single(type): 3 def __call__(self, *args, **kwargs): 4 if hasattr(self,'obj'): # 判断是否有已存在的对象 5 return getattr(self,'obj') # 有就返回 6 obj = super().__call__(*args,**kwargs) # 没有就创建 7 self.obj = obj # 存到类中,方便下次查找 8 return obj 9 10 class Person(metaclass=Single): 11 def __init__(self,name): 12 self.name = name 13 14 # 只会创建初始的对象 15 p = Person('sxc') 16 print(p.name) 17 p1 = Person('zzj') 18 print(p1.name) 19 p2 = Person('zzp') 20 print(p2.name) 21 p3 = Person('lzx') 22 print(p3.name) 23 p4 = Person('zkj') 24 print(p4.name)
元类练习:
1 ''' 2 1.使用元类实现检测类中的所有属性 包括类属性和方法,将名字存储到类的名称空间中 3 例如:有student类 有eat函数 和school属性 ,获取这两个名字 存储到一个叫做attrs的列表中,并将列表作为student类的属性 4 使得student类可以通过访问attrs来获取自身所有的属性名字 5 ''' 6 class MyType(type): 7 def __init__(self,cls,bases,dict): 8 attr = [] 9 for i in dict: # 遍历字典中的类属性 10 if i == 'school' or i == 'eat': 11 attr.append(i) # 把他添加到新的列表中去 12 self.attr = attr # 添加到类属性中 13 super().__init__(cls,bases,dict) 14 15 class Student(metaclass=MyType): 16 school = 'oldboy' 17 18 def eat(self): 19 print('吃东西') 20 21 stu = Student() 22 print(stu.attr)
24