• 元类


    元类

    我们需要牢记这句话:

    一切皆对象:类实际上也是一个对象

    先来举个例子:

    class Person:   #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
        def __init__(self,name):
            self.name = name
        def score(self):
            print('您的分数为99')
    
    p = Person('nick')
    print(p.name)  

    一、什么是元类


    元类:实例化产生类的类

    type是内置的一个元类,所有的类都是type实例化产生

    那么如何找元类呢?

    class Person:   #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
        def __init__(self,name):
            self.name = name
        def score(self):
            print('您的分数为99')
    
    p = Person('nick')
    print(p.name)  
    
    print(type(p))  #<class '__main__.Person'>
    print(type(Person))  #<class 'type'>他们都是由type创建出来的
    print(type(list))   #<class 'type'>他们都是由type创建出来的
    print(type(str))   #<class 'type'>他们都是由type创建出来的
    print(type(object))   #<class 'type'>他们都是由type创建出来的
    print(type(dict))    #<class 'type'>他们都是由type创建出来的
    print(type(type))   #<class 'type'>他们都是由type创建出来的

    二、class底层原理分析


    我们需要深刻理解下面这几段话

    • class 类名会把类构造出来

    • 实际上是元类type实例化产生类这个对象
    • 类实例化产生对象一定是:类名()

    我们举个简单的例子:

    class Person:  #Person类是由type实例化产生,传一堆参数
        pass
    p = Person()   #类实例化产生对象

    此时我们会有疑问???

    type是怎样传一堆参数去实例化产生一个Person类的呢?

    我们打开type()的源码,会发现他是这样的type(object_or_name, bases, dict)因此它需要传3个参数分别是:

    object_or_name:类的名字(是个字符串) class_name = 'Peraon'

    bases: 是他的所有父类、基类们 class_bases = (object,) (object,)是个元组,所以要加个逗号

    dict:类的名称空间(方法和属性们)

    需求:通过type直接产生一个person类,不用class关键字

    class Person: 
        def __init__(self,name):
            self.name = name
        def score(self):
            print('您的分数为99')
    
    p = Person('yjy')
    print(p.name) 
    
    -------------------------------------------------------------------------
    #方式一
    #这是我们要定义的Person类的属性和方法
    school='young_girl'
    def __init__(self,name):
        self.name = name
    def score(self):
        print('您的分数为99')
    
    #用定义Person类
    Person = type('Person',(object,),{'school':'young_girl','__init__':__init__,'score': '您的分数为99'})
    
    p = Person('yjy')  #实例化Person类产生对象p
    
    print(Person.__bases__)  #(<class 'object'>,)
    print(Person.__dict__)   #类的名称空间
    print(p.name)   #yjy
    
    
    #方式二
    l = {}
    
    #exec:函数执行储存在字符串或文件中的 Python 语句
    #exec将''' '''中的变量放到了局部变量l中,待会直接将l放在type()中
    exec('''
    school='young_girl'
    def __init__(self,name):
        self.name = name
    def score(self):
        print('您的分数为99')
    ''',{},l)
    
    Person = type('Person',(object,),l)
    p = Person('yjy')   #实例化Person类产生对象p
    
    print(Person.__bases__)   #(<class 'object'>,)
    print(Person.__dict__)    #类的名称空间
    p.score()  #您的分数为99

    • exec的用法

    exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码

    exec(object[, globals[, locals]])   #这是exec的语法
    • object:必选参数,包含一系列Python代码中的字符串,该字符串会先被解析为一组Python语句,然后再执行
    • globals:可选参数,全局作用域(字典形式)如果不指定,默认globals
    • locals:可选参数,局部作用域(字典形式)如果不指定,默认locals

    三、通过自定义元类控制类的产生


    控制类的产生 == 控制元类的调用过程

    自定义元类;来控制类的产生。可以控制类名,可以控制类的继承父类,控制类的名称空间

    • 自定义元类必须继承type,写一个类继承type这种类都叫元类

    举个例子:

    class Mymeta(type):  #只有继承了type类才能称为元类,否则只是普通的函数
        print('这里可以写一些属性和方法')
    
    #metaclass=Mymeta指定这个类生成的时候,用自己写的Mymeta这个元类
    class Person(metaclass=Mymeta):    
        pass
    
    p = Person()

    练习一:写一个自定义元类,让Person类继承

    class Mymeta(type):
        #def __init__(self,*args,**kwargs):  #和下面这个对应只不过他更正规
        def __init__(self,name,bases,dic):
            #self就是Person类
            print(name)   #Person
            print(bases)  #(<class 'object'>,)
            print(dict)   #Person的名称空间
            
    class Person(object,metaclass=Mymeta):
        school='oldboy'
        def __init__(self,name):
            self.name=name
        def score(self):
            print('分数是100')
    p=Person('yjy')
    print(p.name)   #yjy   而且Mymeta类中的三个print都会打印,就说明他继承了Mymeta

    练习二:写一个自定义元类,限制类名必须以Yjy开头

    #测试一
    class Mymeta(type):
        def __init__(self,name,*args,**kwargs):
            if not name.startswith('Yjy'):
                raise Exception('类名没有以Yjy开头,请做更改')
    
    class Person(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name =name
    
    p = Person('hhh')
    print(p.name)  #Exception: 类名没有以Yjy开头,请做更改
    ------------------------------------------------------------
    #测试二
    class Mymeta(type):
        def __init__(self,name,*args,**kwargs):
            if not name.startswith('Yjy'):
                raise Exception('类名没有以Yjy开头,请做更改')
    
    class YjyPerson(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name =name
    
    p = YjyPerson('hhh')
    print(p.name)   #hhh

    练习三:限制定义的类必须加注释

    #测试一
    class Mymeta(type):
        def __init__(self,name,*args,**kwargs):
            if not self.__dict__['__doc__']:
                raise Exception('定义的类中没有加注释,请做更改')
    
    class YjyPerson(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name =name
    
    p = YjyPerson('hhh')
    print(p.name)
    print(p.__doc__)  #Exception: 定义的类中没有加注释,请做更改
    ---------------------------------------------------------
    #测试二
    class Mymeta(type):
        def __init__(self,name,*args,**kwargs):
            if not self.__dict__['__doc__']:
                raise Exception('定义的类中没有加注释,请做更改')
    
    class YjyPerson(object,metaclass=Mymeta):
        '''我是类里面的注释,我必须要用三引号'''
        def __init__(self,name):
            self.name =name
    
    p = YjyPerson('hhh')
    print(p.name)   #hhh
    print(p.__doc__)   #我是类里面的注释,我必须要用三引号

    四、通过自定义元类控制类的调用过程


    控制类的调用过程 == 对象的产生

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print('我是Mymeta元类中的__call__方法')
    
    class Person(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('我是Person类中的__call__方法')
            
    #自动触发init的执行,此时先去触发元类中的__call__方法
    p = Person('yjy')  #我是Mymeta元类中的__call__方法
    p()  #TypeError: 'NoneType' object is not callable

    ni你需要仔细看这两段代码有什么不同

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print('我是Mymeta元类中的__call__方法')
            obj = object.__new__(self)  #对类进行实例化,并返回该实例,用一个变量obj接收
            obj.__init__(*args, **kwargs)#和__new__一起用才是真正的构造函数
            return obj   #返回obj,这个obj其实就是Person类的名称空间
    
    class Person(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('我是Person类中的__call__方法')
    
    p = Person('yjy')  #我是Mymeta元类中的__call__方法
    p()   #对象加括号调用Person中的__call__方法,我是Person类中的__call__方法
    print(p.name)  #yjy

    练习:把对象所有的属性都变成私有

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print('我是Mymeta元类中的__call__方法')
            obj = object.__new__(self)
            obj.__init__(*args, **kwargs)
            #print(obj.__dict__)   {'name': 'yjy'}
            obj.__dict__ = {'_%s__%s'%(self.__name__,k): v for k, v in obj.__dict__.items()}
            #print(obj.__dict__)    {'_Person__name': 'yjy'}
            return obj
    
    class Person(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('我是Person类中的__call__方法')
    
    p = Person('yjy')
    p()
    # print(p.name)   #AttributeError: 'Person' object has no attribute 'name'
    print(p._Person__name)  #yjy

    五、有了元类之后的属性查找顺序


    • 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中找--->报错
    • 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错

  • 相关阅读:
    Codeforces Round #333 (Div. 2) B. Approximating a Constant Range st 二分
    Codeforces Round #333 (Div. 2) A. Two Bases 水题
    SPOJ 1557. Can you answer these queries II 线段树
    线段树 模板
    Codeforces Round #115 B. Plane of Tanks: Pro 水题
    Codeforces Round #115 A. Robot Bicorn Attack 暴力
    Codeforces Beta Round #51 C. Pie or die 博弈论找规律 有趣的题~
    Codeforces Beta Round #51 B. Smallest number dfs
    Codeforces Beta Round #51 A. Flea travel 水题
    Codeforces Beta Round #51 D. Beautiful numbers 数位dp
  • 原文地址:https://www.cnblogs.com/lulingjie/p/11528998.html
Copyright © 2020-2023  润新知