元类
阅读目录
知识储备
1 code = {
2 字符串
3 }
4
5 exec(code,global_dic,local_dic),
一、其中的字符串内,默认为局部空间如下例:
1 code="""
2 global x
3 x=1
4 y=2
5 """
6 global_dic={'x':10000000}
7 local_dic={}
8 exec(code,global_dic,local_dic)
9
10 print(global_dic) # {'x':1,...}
11 print(local_dic) # {'y':2}
二、产生类有两个方式:
1、一种是用class关键字(背后原理就是调用type)直接生成类:
1 # Chinese = type(...)
2 class Chinese: # class关键字自动拿到类名,括号内的基类,自动调exec函数的执行,把类体代码执行一遍得到一个字典,凑齐三要素自动赋值给type
3 country = 'China'
4
5 def __init__(self, name, age, sex):
6 self.name = name
7 self.age = age
8 self.sex = sex
9
10 def speak(self):
11 print('%s speak Chinese' % self.name)
12
13 print(Chinese) # <class '__main__.Chinese'>
我们用class定义的类来实例化出对象供我们使用,而内置元类type是专门来产生class定义的类
1 # 三大要素
2 # 类名,类的基类,类的局部名称空间
3 class_name = 'Chinese'
4 class_bases = (object,)
5 class_body = """
6 country = 'China'
7 def __init__(self,name,age,sex):
8 self.name = name
9 self.age = age
10 self.sex = sex
11
12 def speak(self):
13 print('%s speak Chinese' % self.name)
14 """
15 class_dic = {}
16 exec(class_body,{},class_dic)
17
18 # 类的三大要素
19 # print(class_name,class_bases,class_dic)
20
21 obj = type(class_name,class_bases,class_dic)
22 print(obj) #<class '__main__.Chinese'>
2、另一种就是自定义元类,再由该元类定制类:
1 #上一步骤中,将最后type实例化的对象名修改成‘Chinese’,即可得到最终结果:
2 Chinese = type(class_name,class_bases,class_dic)
3 print(Chinese) #<class '__main__.Chinese'>
4 lmj = Chinese('lmj',18,'male')
5 print(lmj.name,lmj.age,lmj.sex) #lmj 18 male
三、__call__,调用条件下,会自动触发
class Foo: def __init__(self): pass def __str__(self): pass def __del__(self): pass def __call__(self, *args, **kwargs): print('run',args,kwargs) obj = Foo() obj(1,2,3,a=1,b=2,c=3) # run (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}
注意:调用对象,则会自动将对象所在类的下的绑定方法__call__的执行,然后将对象本身当作第一个参数传给self,将调用对象时括号内的值传给*args与**kwargs。
正题:
类的类是元类(默认type),元类内也必定有__call__方法,用来在调用元类生成类的时候。
当一个类指定他的元类是自定义的(MyMeta),那么就可以通过这个__call__方法来控制自定义化。
自定义类,控制创建类的行为:
1 # 自定义元类 2 # 控制创建类的行为 3 class Mymeta(type): 4 # self = Foo 5 def __init__(self,class_name,class_bases,class_dic): 6 print(class_name) # Foo 7 print(class_bases) # (<class 'object'>,) 8 print(class_dic) #{'__module__': '__main__', '__qualname__': 'Foo', 'x': 1, '__init__': <function Foo.__init__ at 0x000000ABF6FD9C80>, 'f1': <function Foo.f1 at 0x000000ABF6FD9D08>} 9 10 # Foo = Mymeta('Foo',(object,),class_dic) 此处可理解为Mymeta类的实例化 11 class Foo(object,metaclass=Mymeta): 12 x = 1 13 def __init__(self,y): 14 self.y = y 15 16 def f1(self): 17 print('from f1')
进阶完整版如下:
1 # 自定义元类 2 # 控制创建类的行为 3 class Mymeta(type): 4 # self = Foo 5 def __init__(self,class_name,class_bases,class_dic): 6 # print(class_name) 7 # print(class_bases) 8 # print(class_dic) 9 10 if not class_name.istitle(): 11 raise TypeError('类名首字母必须大写') 12 # 重用父类__init__功能 13 14 if not class_dic.get('__doc__'): 15 raise TypeError('类中必须有文档注释') 16 17 super(Mymeta,self).__init__(class_name,class_bases,class_dic) 18 19 20 21 22 # Foo = Mymeta('Foo',(object,),class_dic) 此处可理解为Mymeta类的实例化 23 class Foo(object,metaclass=Mymeta): 24 """ 25 文档注释 26 """ 27 x = 1 28 def __init__(self,y): 29 self.y = y 30 31 def f1(self): 32 print('from f1')
自定义类,控制调用类:
注意:不能用init方法,因为new是在init之前执行的,当new了以后,字典就创建好了,再调init就已经改变不了字典里的内容了,如下:
因此,正确的打开方式如下,使用__new__方法:
1 class Mymeta(type): 2 # self = Foo 3 # 来控制类Foo的创建 4 def __new__(self,class_name,class_bases,class_dic): 5 update_attrs = {} 6 for k,v in class_dic.items(): 7 if not callable(v) and not k.startswith('__'): # 非可调用的值(非函数)和非内置属性 8 update_attrs[k.upper()] = v 9 else: 10 update_attrs[k] = v 11 12 13 if not class_name.istitle(): 14 raise TypeError('类名首字母必须大写') 15 # 重用父类__init__功能 16 17 if not class_dic.get('__doc__'): 18 raise TypeError('类中必须有文档注释') 19 20 return type.__new__(self,class_name,class_bases,update_attrs) 21 22 23 # 控制类Foo的调用,即控制实例化Foo的过程 24 def __call__(self, *args, **kwargs): #self=Foo,args=(111,) 25 # print('+++>') 26 # return 123 27 28 # 1 造空对象obj 29 obj = object.__new__(self) 30 # 2 调用Foo.__init__,将obj连同调用Foo括号内的参数一同传给__init__方法 31 self.__init__(obj, *args, **kwargs) 32 return obj 33 34 # Foo = Mymeta('Foo',(object,),class_dic) 此处可理解为Mymeta类的实例化 35 class Foo(object,metaclass=Mymeta): 36 """ 37 文档注释 38 """ 39 tag = 'China' 40 x = 1 41 def __init__(self,y): 42 self.y = y 43 44 def f1(self): 45 print('from f1') 46 47 # 只要调用类,必产生空对象 48 obj = Foo(111) #此步会触发Foo对象所在类(此例即为Mymeta)之下的__call__() 49 50 51 print(Foo.__dict__) 52 """ 53 {'__module__': '__main__', '__doc__': '\n 文档注释\n ', 'TAG': 'China', 'X': 1, '__init__': <function Foo.__init__ at 0x000000A0F3269D08>, 'f1': <function Foo.f1 at 0x000000A0F3269D90>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>}