元类

阅读目录

知识储备

 

知识储备
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')
View Code

自定义类,控制调用类:

 注意:不能用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>}
__new__实现类属性全大写

 

posted @ 2018-04-18 08:37  MJay_Lee  阅读(328)  评论(0编辑  收藏  举报