Python 元类

什么是元类

源自一句话: 在Python中, 一切皆对象, 而对象都是由类实例化得到的

 1 class OldboyTeacher:
 2     def __init__(self,name,age,sex):
 3         self.name = name
 4         self.age = age
 5         self.sex = sex
 6 
 7     def score(self):
 8         print('%s is scoring' %self.name)
 9 
10 tea1 = OldboyTeacher('hades',18,'male')

对象tea1是调用OldboyTeacher类得到的, 如果说一切皆对象, 那么OldboyTeacher也是一个对象,

只要是对象都是调用一个类实例化得到的, 即OldboyTeacher=元类(....),内置的元类是type

关系:

  1. 调用元类 ----> 自定义的类

  2. 调用自定义的类 ----> 自定义的对象

class关键字创建自定义类的底层工作原理,分为四步:

1. 先拿到类名: 'OldboyTeacher'

2. 再拿到类的基类们: (object,)

3. 然后拿到类的名称空间??? (执行类体代码, 将产生的名字放到类的名称空间也就是一个字典里,补充exec)

3. 调用元类实例化得到自定义的类: OldboyTeacher = type('OldboyTeacher',(object,),{....})

 1 class OldboyTeacher:    # OldboyTeacher=type(...)
 2     school = 'Oldboy'
 3 
 4     def __init__(self, name, age, sex):
 5         self.name = name
 6         self.age = age
 7         self.sex = sex
 8 
 9     def score(self):
10         print('%s is scoring' % self.name)
11 
12 
13 print(OldboyTeacher)

 自定义类的三个关键组成部分:

1. 类名

2. 类的基类们

3. 类的名称空间

不依赖class关键字创建一个自定义类

1. 拿到类名

class_name = 'OldboyTeacher'

2. 拿到类的基类们: (object,)

class_bases = (object,)

3. 拿到类的名称空间

class_dic = {}

class_body = '''
school = 'Oldboy'

def __init__(self,name,age,sex):
    self.name = name
    self.age = age
    self.sex = sex

def score(self):
    print('%s is scoring' %self.name)
'''

exec(class_body, {}, class_dic)

4. 调用type得到自定义的类

OldboyTeacher = type(class_name, class_bases, class_dic)

模板

 1 class Mymeta(type):    #但凡继承了type的类才能称之为自定义的元类,否则就只是一个普通的类
 2     def __init__(self, class_name, class_bases, class_dic):
 3         pass
 4 
 5 class OldboyTeacher(object, metaclass=Mymeta):    #OldboyTeacher=Mymeta('OldboyTeacher', (object,), {....})
 6     school = 'Oldboy'
 7 
 8     def __init__(self, name, age, sex):
 9         self.name = name
10         self.age = age
11         self.sex = sex
12 
13     def score(self):
14         print('%s is scoring' %self.name)

控制类的产生

1. 类名必须用驼峰体

2 类体必须有文档注释,且文档注释不能为空

 1 class Mymeta(type):  # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
 2     def __init__(self, class_name, class_bases, class_dic):
 3         if class_name.islower():
 4             raise TypeError('类名必须使用驼峰体')
 5 
 6         doc = class_dic.get('__doc__')
 7         if doc is None or len(doc) == 0 or len(doc.strip('
 ')) == 0:
 8             raise TypeError('类体中必须有文档注释,且文档注释不能为空')
 9 
10 
11 class OldboyTeacher(object, metaclass=Mymeta):  # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
12     school = 'Oldboy'
13 
14     def __init__(self, name, age, sex):
15         self.name = name
16         self.age = age
17         self.sex = sex
18 
19     def score(self):
20         print('%s is scoring' % self.name)
 1 class Mymeta(type):  # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
 2     pass
 3 
 4 
 5 class OldboyTeacher(object):  # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
 6     school = 'Oldboy'
 7 
 8     def __init__(self, name, age, sex):
 9         self.name = name
10         self.age = age
11         self.sex = sex
12 
13     def score(self):
14         print('%s is scoring' % self.name)
15 
16     def __call__(self, *args, **kwargs):
17         print(self)
18         print(args)
19         print(kwargs)
20 
21 
22 tea1 = OldboyTeacher('egon', 18, 'male')
23 
24 tea1(1, 2, a=1, b=2)  # __call__(tea1,(1,2).{'a':1,'b':2})

总结: 对象之所以可以调用, 是因为对象的类中有一个函数__call__

推导: 如果一切皆对象, 那么OldboyTeacher也是一个对象, 该对象之所可以调用, 肯定是这个对象的类中也定义了一个函数__call__

 1 class Mymeta(type):  # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
 2     def __call__(self, *args, **kwargs):  # self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={}
 3         # 1. 先产生一个空对象
 4         tea_obj = self.__new__(self)  # tea_obj是OldboyTeacher这个类的对象
 5         # 2. 执行__init__方法,完成对象的初始属性操作
 6         self.__init__(tea_obj, *args, **kwargs)
 7         # 3. 返回初始化好的那个对象
 8         return tea_obj
 9 
10 
11 class OldboyTeacher(object, metaclass=Mymeta):  # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
12     school = 'Oldboy'
13 
14     #            tea_obj,'egon',18,'male'
15     def __init__(self, name, age, sex):
16         self.name = name
17         self.age = age
18         self.sex = sex
19 
20     def score(self):
21         print('%s is scoring' % self.name)
22 
23 
24 tea1 = OldboyTeacher('egon', 18, 'male')  # 会触发OldboyTeacher的类(即元类)中的__call__函数

实例化OldboyTeacher, 或者说调用OldboyTeacher:

1. 先产生一个空对象

2. 执行__init__方法, 完成对象的初始属性操作

3. 返回初始化好的那个对象

推导: 调用OldboyTeacher(...)就是在调用OldboyTeacher的类中的__call__, 那么在该__call__中就需要做上述三件事

自定义元类来控制类的调用(即类的实例化过程)

 1 class Mymeta(type):  # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
 2     def __call__(self, *args, **kwargs):  # self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={}
 3         # 1. 先产生一个空对象
 4         tea_obj = self.__new__(self)  # tea_obj是OldboyTeacher这个类的对象
 5         # 2. 执行__init__方法,完成对象的初始属性操作
 6         self.__init__(tea_obj, *args, **kwargs)
 7         # print(tea_obj.__dict__)
 8         tea_obj.__dict__ = {('_%s__%s' % (self.__name__, k)): v for k, v in tea_obj.__dict__.items()}
 9         # 3. 返回初始化好的那个对象
10         return tea_obj
11 
12 
13 class OldboyTeacher(object, metaclass=Mymeta):  # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
14     school = 'Oldboy'
15 
16     def __init__(self, name, age, sex):
17         self.name = name
18         self.age = age
19         self.sex = sex
20 
21     def score(self):
22         print('%s is scoring' % self.name)
23 
24 
25 tea1 = OldboyTeacher('egon', 18, 'male')  # 会触发OldboyTeacher的类(即元类)中的__call__函数

属性查找

 1 class Mymeta(type):  # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
 2     n=444
 3     def __call__(self, *args, **kwargs):  # self=OldboyTeacher这个类
 4         # 1. 先产生一个空对象
 5         tea_obj = self.__new__(self)  # tea_obj是OldboyTeacher这个类的对象
 6         # print(self.__new__ is object.__new__)
 7         # tea_obj=object.__new__(self)
 8 
 9         # 2. 执行__init__方法,完成对象的初始属性操作
10         self.__init__(tea_obj, *args, **kwargs)
11         # 3. 返回初始化好的那个对象
12         return tea_obj
13 
14 
15 class Bar:
16     # n = 33
17     pass
18 
19 
20 class Foo(Bar):
21     # n = 222
22     pass
23 
24 
25 class OldboyTeacher(Foo, metaclass=Mymeta):  # OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
26     # n = 111
27     school = 'Oldboy'
28 
29     def __init__(self, name, age, sex):
30         self.name = name  # None.name='egon'
31         self.age = age
32         self.sex = sex
33 
34     def score(self):
35         print('%s is scoring' % self.name)
36 
37     def __new__(cls, *args, **kwargs):
38         # print('=====>')
39         return super().__new__(cls)
40 
41 
42 tea1 = OldboyTeacher('egon', 18, 'male')
43 print(tea1.n)
原文地址:https://www.cnblogs.com/earon/p/9544709.html