初识python元类(metaclass)
听过一句话,在python中一切皆对象,而在python3中统一了类和类型。
我们可以用type方法查看一个变量(对象)的类型,即是查看对象的类:
x=2 y='dasd' z=[] class Student: school='jjjj' def __init__(self,name): self.name=name print(type(x),type(y),type(z),type(Student),) #结果:<class 'int'> <class 'str'> <class 'list'> <class 'type'>
当然在了解元类之前,我们需要了解一些姿势点,方便以后运用。
1:python内置函数exec(解释:独立(在其他语言中不一定独立)执行一段python字符串代码,并留下一些东东)
exec(a,b,c) 我们一般是这样用。
a:要执行的字符串代码
b:a中可能用到的外部变量(字典格式,一般是全局的)
c:返回执行a后产生的名称空间(不要问我是什么,字典格式)
name='wx' a=''' k=name j='das' def pp(): pass ''' b={'name':name} c={} exec(a,b,c) print(c) #结果:{'k': 'wx', 'j': 'das', 'pp': <function pp at 0x0569A228>} nn
2.__call__方法(类中定义这方法时,实例化出的对象被调用时会触发此方法)
class Teacher: def __call__(self, *args, **kwargs): print('__call__ 执行') print(args) return 444 obj=Teacher()#实例化 print(obj('llll'))#调用对象,当然也可传参 '''结果: __call__ 执行 ('llll',) 444 '''
3.__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple,可变的也行),
提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclass。还有许多用途。
这里我们主要是用其创建一个新的对象.
class UpperStr(str): def __new__(cls, s): return super().__new__(cls,s.upper()) print(UpperStr('asdasd')) #结果:ASDASD
class MyNew: def __init__(self,name,age): self.name=name def __new__(cls, *args, **kwargs): obj=super().__new__(cls)#造一个空对象,也调用了MyNew的__init__方法 # #为对象加个属性 obj.new_age=kwargs.get('age') return obj obj=MyNew(name='da',age=18) print(obj,vars(obj),) #结果:<__main__.MyNew object at 0x057D4070> {'new_age': 18, 'name': 'da'}
''' 回到正题,我们查看类type(Student)时看到的Student类的类为type,也就是说Student类是由type类产生的, 我们把产生类的类称为元类,python中默认的元类是type 当我我们定义类的时候 ''' class Student(object,metaclass=type): #其实这里指定了类由哪个元类产生,默认是type #即是Student=type(.....) 由type类实例化而来 pass
'''
那我们为毛要学习元类:为了掌握一种定义类的方式,在定义类时加一下限定条件。 我们定义类时,一种方式是用关键字class 这里我们来模拟一下class创建类的过程, 首先创建类需要什么? 1:类名 2:基类(父类) 3:类体代码(一堆字符串) ''' #类名 class_name='Student' #类的父类 class_bases=(object,) #类体 class_body=""" school='dh' def __init__(self,name,age): self.name=name self.age=age def study(self): print('%s is studying' %self.name) """ #类体定义的名字都会存放于类的名称空间中,与exec相似 class_dic={} exec(class_body,{},class_dic) print(class_dic) #结果{'school': 'dh', '__init__': <function __init__ at 0x04AEA2B8>, 'study': <function study at 0x04AEA270>}
那么调用元类type(也可以自定义)来产生类Student
''' 那么调用元类type(也可以自定义)来产生类Student ''' Student=type(class_name,class_bases,class_dic) print(Student,type(Student),isinstance(Student,type),Student.school) #结果:<class '__main__.Student'> <class 'type'> True dh
通过上面产生的类过程,我们就可以自定义一个元类,即是写一个类,继承type,复写它的一些方法,如下:
class Mymeta(type): # 继承默认元类的一堆属性 def __init__(self, class_name, class_bases, class_dic): if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) class student(object,metaclass=Mymeta): school = 'dh' def __init__(self, name, age): self.name = name self.age = age def study(self): print('%s is studying' % self.name) #则在定义阶段 会报错:TypeError: 类名首字母必须大写
这样就自定义了一个元类,并控制产生类的条件是类名必须是title。
当然,我们自定义的元类必须继承type,是为了继承默认元类的一堆属性。
其调用过程如下:
class Mymeta(type): # 继承默认元类的一堆属性 def __init__(self, class_name, class_bases, class_dic): if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) def __call__(self, *args, **kwargs): # self=Student print(self, args, kwargs) # <class '__main__.Student'> ('alan', 18) {} # 1、调用self,即Student下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj # 若没有,则按属性查找方式往上找 # 给对象加一些属性,即是调用init方法 obj = self.__new__(self, *args, **kwargs) # 2、一定记得返回obj,因为实例化Student(...)取得就是__call__的返回值 return obj class Student(object,metaclass=Mymeta): school = 'dh' def __init__(self, name, age): self.name = name self.age = age def study(self): print('%s is studying' % self.name) def __new__(cls, *args, **kwargs): obj = object.__new__(cls) obj.kkk='dasdas' cls.__init__(obj,*args, **kwargs) return obj obj=Student('alan',18) print(obj,vars(obj))#<__main__.Student object at 0x05514850> {'kkk': 'dasdas', 'name': 'alan', 'age': 18}
这样Mymeta就是我们自定义的元类,其中调用过程,Student(。。。)即是触发Student类的类(即是Mymeta)的__call__方法,而在__call__方法中调用了__new__()方法,创建了一个对象,并为对象加了一些属性,这样类Student的实例化过程就完成。
如此,我们可以在类的定义阶段对类加一些限制。
如果觉得不错,动动你们发财的小手,求个订阅!!!