元类以及反射
元类定义
''' 什么是元类: 就是产生类的类 简称为元类 metaclass 用途:对类的产生设定一些规定, 作用: 创建新的元类继承type,通过覆盖"__init__"完成对类的限制. 如例子对类名和方法的限制 什么时候用 : 需要对类进行一些限制的时候 '''
type的两种用法
type 的两种意思: 一种是类型的 比如 print(isinstance('a', str)) 二是类的元类 type(对象) 就是对象的类型 type(类名,父类们,名称空间) 产生的 就是一个新的类 名称空间实际上就是一个字典 用来存贮数据的 3:类的组成部分 1)类名 2)父类们 3)名称空间
type接收三个参数分别是: classname: 要创建的class 的名称 object:要创建类的父类所组成的元组 sttr_dict: 要创建类的属性 type返回一个class,我们接收并赋值到一个变量上,现在这个变量就指向我们所创建的类,我们可以通过这个变量来使用类
自定义元类
class UpperAttrMetaClass(type):
def __new__(cls,class_name,class_parents,class_attr, *args, **kwargs):
print("__new__")
class_attr['name'] = "jiao"
return type.__new__(cls,class_name,class_parents,class_attr)
def __new__(cls,class_name,class_parents,class_attr, *args, **kwargs):
print("__new__")
class_attr['name'] = "jiao"
return type.__new__(cls,class_name,class_parents,class_attr)
def __init__(self,*args,**kwargs):
print("__init__")
super().__init__(*args, **kwargs)
self.__cache = {}
print("__init__")
super().__init__(*args, **kwargs)
self.__cache = {}
def __call__(self, *args, **kwargs):
print("__call__")
if args in self.__cache:
return self.__cache[args]
else:
obj = super().__call__(*args)
self.__cache[args] = obj
return obj
print("__call__")
if args in self.__cache:
return self.__cache[args]
else:
obj = super().__call__(*args)
self.__cache[args] = obj
return obj
class A(metaclass=UpperAttrMetaClass):
def __init__(self,name):
self.name = name
print("a.__init__")
元类中__init__ __new__ __call__ 函数
1)__init__在元类中的应用
在实例化对象时会自动调用__init__方法,会将对象本身作为第一个参数传递过去 因为类也是对象 在实例化类对象时也会自动调用__init__方法,会见类对象作为第一个参数传递进去, 也会传递三个参数 分别是 类名称 父类们 名称空间 __init__在元类中的作用: 创建新的元类继承type,通过覆盖"__init__"完成对类的限制.
class A: def __init__(self,name): self.name= name a= A()# 实例化对象 class MyMetaclass(type): def __init__(self,class_name,bases,name_dict): # ''' 自定义元类 :param class_name: 类名称 :param bases: 父类们 :param name_dict: 名称空间 ''' pass class B(metaclass=MyMetaclass): # 此时运行到class 就是实例化类对象B了 pass
# 案例 限制类名必须首字母大写 控制类中方法名必须全部小写 # 分析:那么可以元类中限制类的创建条件 # 因为类名和属性和方法都是存储到类中名称空间的 类名下对应下是属性和方法 那么可以通过操作名称空间进行限制 class MyMetaclass(type): def __init__(self,class_name,bases,name_dict): # 元类的self代表的就是类对象 super().__init__(class_name,bases,name_dict) # 对父类进行初始化 # 对类名首字母进行限制 必须大写字母 if not class_name.istitle(): print('类名必须大写') # else: 这一行可不需要 raise Exception # 否则就报异常 # 取出类中的属性和方法 for k in name_dict: if str(type(name_dict[k])) == "<class 'function'>": # "<class 'function'>" 就是类的方法 if not k.islower(): print('方法首字母必须小写') raise Exception class student(object,metaclass=MyMetaclass): # 类名小写报错 提示 类名必须大写 根据上面元类的限制类名的方法 NAME = 10 def say_hai(self): print("============") # class Student(metaclass=MyMetaclass): # 方法名大写报错 提示 方法名必须小写 # name = 10 # def Say(self): # print('111111111111111111111')
元类中的__new__方法
__new__创建类对像的时候执行 先于__init__运行 作用:创建类对象 注意:如果覆盖了`__new__` 一定也要调用type中的`__new__`并返回执行结果 __new__与__init__的区别 :
1)先于__init__执行
2)__new__是创建类对象的(指的是元类的对象) __init__是实例化类对象的
class MyMetaclass(type): def __init__(self,class_name,bases,name_dict): pass def __new__(cls, *args, **kwargs): # pass # cls表示类自己 即MyMetaclass return type.__new__(cls,*args,**kwargs) class People(metaclass=MyMetaclass): pass print(People)
# __new__作用是创建类的 必须要返回值 没有无法创建对象的 当把return type.__new__(cls,*args,**kwargs)注释掉
# print(People) 的结果是None
# 案例 要求每个类中必须含有__doc__属性 就是必须要有注释的意思 class MyMetaclass(type): def __init__(self,class_name,bases,name_dict): super().__init__(class_name,bases,name_dict) # if not self.__doc__: # raise Exception # 两种方法都行 # 或者 if not('__doc__' in name_dict and name_dict['__doc__']): # __doc__在名称空间还里 raise Exception # class A(metaclass=MyMetaclass): # pass # 无注释 报错 class B(metaclass=MyMetaclass): #有注释不报错 ''' '''
元类中的__call__
执行时机:在元类中,调用类时执行 作用:控制对象的创建过程 案例 用__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 People(metaclass=SingletonMetaClass): def __init__(self,name,age,gender): self.name=name self.age =age self.gender =gender def say(self): print('my name is %s my aunt is 龙妈'% self.name) class Stu(metaclass=SingletonMetaClass): def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender def say(self): print('my name is %s my aunt is 龙妈' % self.name) p1 = People('jeck',18,'man') print(p1) # 地址都是一样的 p2 = People('tom',22,'man') print(p2) # 地址都是一样的
# 案例 单例模式制作一个QQ播放器案例 class MyMetaclass(type): def __init__(self,class_name,bases,name_dict): super().__init__(class_name,bases,name_dict) 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 QQplayer(metaclass=MyMetaclass): def __init__(self,voice_value,repeat=None): # repeat 重复的意思 self.voice_value =voice_value self.repeat = repeat def play(self,file_path): # 播放功能 if hasattr(self,'file_path'): # self.stop() print('正在播放%s' % file_path) self.file_path = file_path def stop(self): # 停止播放 print('停止播放%s' % self.file_path) q1 = QQplayer(100,True) q1.play('多远多要在一起') q2 = QQplayer(80,True) q2.play('泡沫') # 正在播放多远多要在一起 # 停止播放多远多要在一起 # 正在播放泡沫 # 1. hasattr(object, 'name') # # 判断object对象中是否存在name属性,当然对于python的对象而言,属性包含变量和方法;有则返回True,没有则返回False; # 需要注意的是name参数是string类型,所以不管是要判断变量还是方法,其名称都以字符串形式传参;getattr和setattr和delattr也同样;
什么是单例:
某个类如果只有一个实例对象,那么该类成为单例类
单例的好处:
当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源
案例:
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
反射
面向对象中的反省 指的是,一个对象必须具备,发现自身属性,以及修改自身属性的能力;
一个对象在设计初期,可能考虑不够周全后期需要删除或修改已经存在的属性, 和增加属性
涉及到的方法:
hasattr 判断是否存在某个属性 getattr 获取某个属性的值 setattr 新增或修改某个属性 delattr 删除某个属性
案例:
class MY_CMD: def dir(self): os.system("dir") def ipconfig(self): os.system("ipconfig") cmd = MY_CMD() while True: name = input("请输入要执行的功能:") if hasattr(cmd,name): method = getattr(cmd,name) print(method) method() else: print("sorry this method is not exists....!")