zoukankan      html  css  js  c++  java
  • Python元类

    默认type元类的底层工作原理:

    #方式一:用class关键字去创建,用的就是默认的元类
    class People:    #People=type(...)
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eatting'%self.name)
    
    #查看对象obj的类
    obj=People('张三',34)
    print(type(obj))
    #查看People的类
    print(type(People))  #<class 'type'> 说明People的类是
    
    ---------------------------------------------------------------------------
    #分析默认元类的底层实现原理
    class_name='People'
    class_bases=(object,)   #元祖格式
    class_dic={ }
    class_body='''  
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    
    def eat(self):
        print('%s is eatting'%self.name)
    
    '''
    利用exec通过类体代码得到class_dic,让字典里面有值
    exec(class_body,{},class_dic)   #exec模拟类定义阶段造名称空间的过程
    准备好创建类的三要素
    print(class_name)
    print(class_bases)
    print(class_dic)
    
    People=type(类名,基类,类的名称空间)
    根据一切皆对象的观念,用type(people)发现People实质是type()一个类实例化得到的结果
    验证people是调用type元类实例化得到了这个效果(是用的默认的元类type)
    实质:class People():class关键字调用的底层原理
    People=type(class_name,class_bases,class_dic) ===   等同于class People():
    print(People)

    自定义元类:初始模板

    #方式二:用自定义元类
    #People=type(class_name,class_bases,class_dic) 默认的元类
    class Mymeta(type):  #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        # 重点self就是People类,这里其实就是People=Mymeta(...)实例化得到的一个对象
        #触发init方法的时候,会将people当做第一个参数出入,即self 就是 People
        def __init__(self,class_name,class_bases,class_dic):   #自己定义重写init
            print(self)   #现在就是People这个类  <class '__main__.People'>
            print(class_name) #People
            print(class_bases)  #(<class 'object'>,)
            print(class_dic)  #People类体代码执行过程中产生的一堆名字
            super(Mymeta,self).__init__(class_name,class_bases,class_dic)
    
    
    # 分析用class自定义类的运行原理(而非元类的的运行原理):
    # 1、拿到一个字符串格式的类名class_name='People'
    # 2、拿到一个类的基类们class_bases=(obejct,一堆类A,B等..) 基类默认继承object类
    # 3、执行类体代码,拿到一个类的名称空间class_dic={...},类体执行过程中产生的名字
    # 4、调用People=type(class_name,class_bases,class_dic)
    # (继承的父类,metaclass=type默认的元类)
    class People(object, metaclass=Mymeta):  # People=Mymeta(class_name,class_bases,class_dic)
        country = 'China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eatting'%self.name)

    应用一:(控制class定义类的过程)

    # 自定义类应用一:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程(限定类首字母大写,必须要有文档注释)
    
    class Mymeta(type):
    
        def __init__(self,class_name,class_bases,class_dic):   #自己定义重写init
            if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip())==0:   #限定类里面必须要有文档注释
                raise TypeError('类中必须要有文档注释,并且文档不能为空')
            if not class_name.istitle():              #限定类名首字母必须大写
                raise TypeError('类名首字母必须大写')
    
            super(Mymeta,self).__init__(class_name,class_bases,class_dic)   #重用父类功能
    
    class People(object, metaclass=Mymeta):  # People=Mymeta(class_name,class_bases,class_dic)
        '''这是People类'''
        country = 'China'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eatting'%self.name)

    应用二:

    __call__ 实现让对象,变成一个可调用的对象,即元类里面如果想实现people()一定是对象people对应的类中有__call__的方法才行,
    默认type元类里面有的

    #简单说明下我们分析的开始
    # 实现目的: obj()
    # class Foo:
    #     pass
    #
    # obj=Foo()
    # obj()   #报错: 'Foo' object is not callable
    #改进:在Foo内定义一个call方法
    # class Foo:
    #     def __call__(self, *args, **kwargs):
    #         print(self)  #<__main__.Foo object at 0x031A1D30>
    #         print(args) # ()
    #         print(kwargs) #{}
    #
    # obj=Foo()
    # obj()

    __call__方法在元类例如的应用实例1:控制对象(指的就是People对象)的产生过程

    #思考People也是对象,想要控制People对象的调用过程,就是自定义类的实例化过程,
    #即通过控制对象People类Mymeta里面的call方法
    class Mymeta(type):
    
        def __call__(self, *args, **kwargs):  #最终接收到的值只要传给People里面init方法里面用的
            # print(self) # self是People
            # print(args) #('egon',)
            # print(kwargs) #{'age': 18}
            # return 123  如果这里不定义,name print(obj)拿到的返回值就是None
            #1、先造出一个People的空对象
            obj=self.__new__(self)     #People要找new这个属性,默认用type里面的__call__
    
            #2、为该对空对象初始化独有的属性(为对象初始化功能的属性)
            self.__init__(obj,*args,**kwargs)  #没有自定传值一说,现在是控制底层的创建过程,最终是将值传给People的init用
    
            #3、返回一个初始好的对象
            return obj
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        # def __new__(cls, *args, **kwargs):  #为了验证查找new的顺序(先找自己-》再找类Mymeta-》再找type)
        #     print('People自己的new属性')
        def __new__(cls, *args, **kwargs):
            # print(cls)
            # cls.__new__(cls)  #错误的写法,如果自由里面这样写,会变成死循环
            obj=super(People,cls).__new__(cls)  #可以通过super让其去父类里面找
            return obj
    
    
    #将people看成是一个对象,people()能调用说明People对象类中有__cal
    obj=People('egon',age=18)   #‘egon’->args,
    print(obj.__dict__)    #调对象拿到的返回值是None,如果不设置返回值,就是None

    注意几点:

    第一点:__call__:只有在调用对象时才会触发即People()

     其实上面加了元类后,与class直接调用元类没有太多变动,

  • 相关阅读:
    第五周作业
    第四周作业
    第三周作业
    第二周作业
    Linux常用命令使用格式及实例
    总结linux哲学思想
    配置环境变量,实现执行history的时候可以看到执行命令的时间
    安装Centos7.6操作系统后,创建一个自己名字的用户名,并可以正常登陆
    各系列的Linux发行版及不同发行版之间的联系与区别
    python2使用Tkinter制作GUI内嵌matplotlib时,找不到NavigationToolbar2Tk的问题
  • 原文地址:https://www.cnblogs.com/yangzhizong/p/9318039.html
Copyright © 2011-2022 走看看