class Student(): gender = 'man' def __init__(self,name,age): self.name = name self.age = age def say(self): print('你好') s1 = Student('jack',18) #hasattr(对象,字符串属性名) 判断对象是否存在某个属性和方法(包括类中的) print(hasattr(s1,'say')) # True print(hasattr(s1,'gender')) # True print(hasattr(s1,'say')) # True print(hasattr(s1,'run')) # False # getattr(对象,字符串属性名,默认值) 获取对象某个属性的值(方法是地址),如果不存在返回默认值(也可不设置) print(getattr(s1,'say')) # <bound method Student.say of <__main__.Student object at 0x000001A3C43E4080>> print(getattr(s1,'name')) # jack print(getattr(s1,'na',1)) # 1 # setattr(对象,字符串属性名,属性值) 对象新增或修改某个属性(方法忽略) setattr(s1,'name','rose') setattr(s1,'classes',11) print(getattr(s1,'name')) # rose print(getattr(s1,'classes')) # 11 # delattr对象,字符串属性名) 对象删除某个属性(方法忽略) delattr(s1,'name') print(getattr(s1,'name',1)) # 1
3.使用场景:
1)反射其实就是对属性的增删改查,但是如果直接使用内置的dict来操作,语法繁琐,不好理解
2)另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法
""" 反射被称为框架的基石,为什么 因为框架的设计者,不可能提前知道你的对象到底是怎么设计的 所以你提供给框架的对象 必须通过判断验证之后才能正常使用 判断验证就是反射要做的事情, 当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装 需求:要实现一个用于处理用户的终端指令的小框架 框架就是已经实现了最基础的构架,就是所有项目都一样的部分 """ import plugins # Py文件 # 框架已经实现的部分 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 列出所有文件....")
# settings文件内容 """该文件作为框架的配置文件""" # 作为框架使用者 在配置文件中指定你配合框架的类是哪个 CLASS_PATH = "libs.plugins.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)
libs/plugins.py
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 列出所有文件....")
settings.py
"""该文件作为框架的配置文件""" # 作为框架使用者 在配置文件中指定你配合框架的类是哪个 CLASS_PATH = "libs.plugins.LinuxCMD"
myframework.py
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)
直接写import 称之为静态导入 建立在一个基础上:提前已经知道有这个模块 动态导入 指的是 在需要的任何时候 通过指定字符串类型的包名称来导入需要的模块 import importlib mk = importlib.import_module(m_name) # 模块名(一般为模块py文件的绝对路径) mk 即导入成功的模块 """ 该方式常用在框架中 因为框架设计者不可能提前预知后续需要的模块和类
对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的,默认情况下所有类的元类都是type,类对 象 是由type实例化产生的
2.
class Person: pass p = Person() print(type(p)) # <class '__main__.Person'> print(type(Person)) # <class 'type'>
3.用type 来实例化产生一个类:
type(类名,父类元组,名称空间字典) #返回一个新的类 type(对象) #将会返回这个对象的类型 所以:我们可以总结出 当你定义一个class时,解释器会自动调用type来完成类的实例化
4.元类目的:
学习元类的目的:
高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写
类也是对象,也有自己的类,
我们的需求是创建类对象做一些限制
想到了初始化方法 我们只要找到类对象的类(元类),覆盖其中 init方法就能实现需求
当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
# 自定义元类 class MyMetaClass(type): pass # 绑定元类 class Person(metaclass=MyMetaClass): pass
2.__new__():
元类中的new方法会在创建类对象时执行,并且先于init(准确的说是创建类对象后)方法 作用是创建一个类对象 class A(metaclass=MyMetaClass): pass 1.执行MyMetaClass的`__new__`方法 拿到一个类对象 2.执行MyMetaClass的`__init__` 方法 传入类对象以及其他的属性 ,进行初始化 注意:如果覆盖了`__new__` 一定也要调用type中的`__new__`并返回执行结果 且必须是 对应的类对象,不然无法触发__init__执行 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)
3.__init__()
实例化对象时会自动执行类中的`__init__`方法, 类也是对象 ,在实例化类对象时会自动执元类中的`__init__`方法(注意与new方法区分,先执行new方法在执行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
案例: 限制类名必须首字母大写 控制类中方法名必须全部小写 class MyMetaClass(type): def __init__(self,class_name,bases,name_dict): super().__init__(class_name,bases,name_dict) # 类名必须首字母大写 否则直接抛出异常 if not class_name.istitle(): print("类名必须大写 傻x!") raise Exception # 控制类中方法名必须全部小写 for k in name_dict: if str(type(name_dict[k])) == "<class 'function'>": if not k.islower(): raise Exception pass # 会自动调用其元类中的 __init__ 方法传入 类对象本身 类名称 父类们 名称空间 class Student(object,metaclass=MyMetaClass): # MyMetaClass("Student",(object,),{}) NAME = 10 def say(self): print("SAY") pass
4.__call__()
当你调用类对象时会自动珍惜元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数(元类绑定的类加()执行元类中的call方法) 覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建 并返回其返回值 元类实现单例模式 什么是单例: 某个类如果只有一个实例对象,那么该类成为单例类 单例的好处: 当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源 案例: class SingletonMetaClass(type): #创建类时会执init 在这为每个类设置一个obj属性 默认为None def __init__(self,a,b,c): super().__init__(a,b,c) self.obj = None # 当类要创建对象时会执行 该方法 def __call__(self, *args, **kwargs): # 判断这个类 如果已经有实例了就直接返回 从而实现单例 if self.obj: return self.obj # 没有则创建新的实例并保存到类中 obj = type.__call__(self,*args,**kwargs) self.obj = obj return obj
# 单例元类 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()