元类补充
Posted hzls
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了元类补充相关的知识,希望对你有一定的参考价值。
什么是元类 ?
class OldboyTeacher(object): school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name) t1 = OldboyTeacher(‘jason‘)
# 对象t1是由类OldboyTeacher实例化得到
那么类也是对象,它又是谁实例化得到的呢?
# 分别查看对象t1和OldboyTeacher的类型 print(type(t1)) print(type(OldboyTeacher)) # 结果为: <class ‘__main__.OldboyTeacher‘> <class ‘type‘>
结论1:元类就是产生类的类,默认情况下type就是所有类的元类
不依赖class关键字创建类
根据第一个结论我们能理出两条对应关系
1.调用元类得到自定义的类
2.调用自定义的类得到自定义的类的对象
现在我们来看第一对关系,调用元类来得到自定义的类,都需要哪些参数(OldboyTeacher=type(...),括号内传什么?)
我们自定义一个类的时候都有哪些关键的组成部分:
1.类名
2.类的父类
3.类的名称空间
就以第一阶段的OldboyTeacher类为例,calss关键字创建自定义类的步骤
""" 1.获取类名(OldboyTeacher) 2.获取类的父类(object,) 3.执行类体代码获取产生的名称空间(如何获取???) 4.调用元类得到自定义类OldboyTeacher = type(class_name,class_bases,...) """
补充 exec
exec : 用于执行字符串形式的python代码 只要符合python都能执行 ,并且可以指定将执行产生的名字放入某个名称空间
class_body = """ school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name) """ class_dic = class_global = exec(class_body,class_global,class_dic) # class_global一般情况下都为空,除非在字符串代码内部用global关键字声明,才会将产生的名字丢到class_global全局名称空间中 print(class_dic) ‘school‘: ‘oldboy‘, ‘__init__‘: <function __init__ at 0x000000B5D2771EA0>, ‘run‘: <function run at 0x000000B5DB5B7400>
有了这个exec方法后,我们就可以不依赖于calss关键字创建自定义类
# 类名 class_name = ‘OldgirlTeacher‘ # 类的父类 class_bases = (object,) # 注意必须是元祖,逗号不能忘 # 名称空间 class_body = """ school = ‘oldgirl‘ def __init__(self,name): self.name = name def run(self): print(self.name) """ class_dic = exec(class_body,,class_dic) #调用元类创建自定义类 OldgirlTeacher = type(class_name,class_bases,class_dic) print(OldgirlTeacher) # 结果为:<class ‘__main__.OldgirlTeacher‘> # 并且它可以访问自身的属性和方法,并实例化产生对象 print(OldgirlTeacher.school) print(OldgirlTeacher.run) # 结果为: """ oldgirl <function run at 0x000000229B157378> """ obj = OldgirlTeacher(‘jason‘) print(obj.school) obj.run() """ oldgirl jason """
class Mymeta(type): # 必须是继承了type的类才是自定义元类 pass class oldboyTeacher(metaclass=Mymeta): # 通过metaclass可以指定类的元类 school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name)
2.__call__
思考: 一个类的对象加括号调用会执行该对象父类中的__call__方法,那么类也是对象,它在加括号实例化对象的时候,是不是也应该走它父类的__call_方法?
class Mymeta(type): def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) class OldboyTeacher(object,metaclass=Mymeta): school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name) obj = OldboyTeacher(‘jason‘) """ 打印结果: <class ‘__main__.OldboyTeacher‘> (‘jason‘,) """
思考: 类加括号实例化对象的时候,有哪几个步骤?
1.创建一个该类的空对象
2.实例化该空对象
3.将实例化完成的空对象返回给调用者
# 也就是说__call__里面需要做三件事 class Mymeta(type): def __call__(self, *args, **kwargs): # 1.产生空对象 # 2.初始化对象 # 3.返回该对象 # 那我先做最后一件事,返回一个123,发现 return 123 obj = OldboyTeacher(‘jason‘) print(obj) # 结果就是123
class Mymeta(type): def __call__(self, *args, **kwargs): # 1.产生一个空对象 obj = self.__new__(self) # 2.实例化该对象 self.__init__(obj,*args,**kwargs) # 3.返回该对象 return obj # 关于这个__new__,我们是不是不知道是个啥,我这里直接告诉你,它就是用来创建空对象的
思考 : 这是类加括号产生对象的过程,那么我元类加括号产生类的过程是不是也应该是这个三步
1.产生一个空对象(指类)
2.实例化该空对象(实例化类)
3.将实例化完成的类对象返回
那依据上面的推导,self.__new__就是关键了,我可以在我的自定义元类里面定义一个__new__方法,看看它到底是个啥
class Mymeta(type): def __new__(cls, *args, **kwargs): print(cls) print(args) print(kwargs) class OldboyTeacher(object,metaclass=Mymeta): school = ‘oldboy‘ def __init__(self, name): self.name = name def run(self): print(‘%s is running‘ % self.name) """ <class ‘__main__.Mymeta‘> (‘OldboyTeacher‘, (object,), ‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘OldboyTeacher‘, ‘school‘: ‘oldboy‘, ‘__init__‘: <function OldboyTeacher.__init__ at 0x000000323CEB9510>,
‘run‘: <function OldboyTeacher.run at 0x000000323CEE7158>) """
我们发现__new__里面的*args参数接收到了三个位置参数,并且很容易辨认它们对应的就是类名,类的父类,类体代码执行后的名称空间
那么我们可不可以将__new__()的形参换一种写法
class Mymeta(type): def __new__(cls, class_name,class_bases,class_dic): print(class_name) print(class_bases) print(class_dic) # 这里需要记住的是,必须在最后调用元类type中的__new__方法来产生该空对象 return type.__new__(cls,class_name,class_bases,class_dic) class OldboyTeacher(metaclass=Mymeta): school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name)
验证:
class Mymeta(type): def __new__(cls, class_name,class_bases,class_dic): print(class_name) print(class_bases) print(class_dic) class_dic[‘xxx‘] = ‘123‘ if ‘school‘ in class_dic: class_dic[‘school‘] = ‘DSB‘ return type.__new__(cls,class_name,class_bases,class_dic) class OldboyTeacher(metaclass=Mymeta): school = ‘oldboy‘ def __init__(self,name): self.name = name def run(self): print(‘%s is running‘%self.name) print(OldboyTeacher.xxx) # 发现可以打印出来 123 print(OldboyTeacher.school) # DSB
结论:
由此我们就可以通过自定义元类,并重写__new__方法来拦截类的创建过程,在类被创建出来之前进行一系列其他操作
以上是关于元类补充的主要内容,如果未能解决你的问题,请参考以下文章
python3全栈开发-内置函数补充,反射,元类,__str__,__del__,exec,type,__call__方法