• 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)
  • 相关阅读:
    Eureka相关相关接口和代码位置
    Zookeeper(4)---ZK集群部署和选举
    Zookeeper(3)---java客户端的使用
    Zookeeper(2)---节点属性、监听和权限
    玩转百度地图API(地图,坐标,标记,添加控件,2D图,混合图,智能搜索,地址解析器,信息窗口)
    HTML+CSS系列:CSS选择器(标签、ID、类、通配符、后代、子元素、并集、伪类)
    Git系列:常用命令
    Linux系列:快捷键、目录结构、用户目录
    mybatis-plus系统化学习之更新-AR-主键-service
    mybatis-plus系统化学习之查询专题
  • 原文地址:https://www.cnblogs.com/earon/p/9544709.html
Copyright © 2020-2023  润新知