• 元类


    一、元类

    1.1 什么是元类

    1. 在python中一切皆对象,类实际上就是一个一个的对象。

    2. 我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类)

    3. type是内置的一个元类,所有的类都是由type实例化得到的。

    4. 如下,Person类也是一个对象,那么他一定是由一个类的实例化得到的,这个类就叫做元类。

      class Person:
          def __init__(self,name):
              self.name = name
          def score(self):
              print('分数是100')
      a = Person  # 类也是一等公民
      p = a('ocean')
      print(p.name)
      
      

    1.2 如何找元类

    #利用print进行打印出
    #type类是产生所有类的元类
    print(type(p))#<class '__main__.Person'>
    print(type(Person))#<class 'type'>
    print(type(dict))#<class 'type'>
    print(type(list))#<class 'type'>
    print(type(str))#<class 'type'>
    print(type(type))#<class 'type'>
    

    二、class底层原理分析

    1. class 类名,会把类构造出来。实际上实例化产生类,也是一个对象

    2. 类实例化产生对象,一定是:类名()

    3. Person类是由type实例化产生,传入一堆参数

      class Person:
          def __init__(self,name):
              self.name = name
          def score(self):
              print('分数是100')
      a = Person  # 类也是一等公民
      p = a('ocean')
      print(p.name)
      
    4. type(object_or_name, bases, dict)

      1. object_or_name,类的名字,是一个字符串
      2. bases:是所有的父类(基类)
      3. dict:名称空间是一个字典
    5. class底层就是调用type来实例化产生类(对象)

    2.1 通过type来直接生成类

    可以不用class关键字

    type(object_or_name, bases, dict)

    1. object_or_name,类的名字,是一个字符串
    2. bases:是所有的父类(基类)
    3. dict:名称空间是一个字典

    方法一

    Person = type('Person',(object,),
                  {'school': 'hnsf'})
    print(Person.__dict__)
    #{'school': 'hnsf', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
    
    

    方法二

    exec("""
    school = 'hnsf'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('分数100分')
    """,{},l)
    
    Person = type('person',(object,),l)
    print(Person.__dict__)
    #{'school': 'hnsf', '__init__': <function __init__ at 0x000001C0FFD17318>, 'score': <function score at 0x000001C0FFD17168>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'person' objects>, '__weakref__': <attribute '__weakref__' of 'person' objects>, '__doc__': None}
    
    

    法三

    def __init__(self,name):#魔法方法要先写好
        self.name = name
    Person = type('Person',(object,),
                  {'school': 'hnsf', '__init__':__init__})
    print(Person.__dict__)
    #{'school': 'hnsf', '__init__': <function __init__ at 0x00000226235673A8>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
    print(Person.__bases__)#(<class 'object'>,)
    
    
    

    三、通过元类来控制类的产生

    3.1自定义元类

    自定义元类来控制类的产生,可以控制类名,可以控制类的继承父类

    控制类的名称空间

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

    class Mymeta(type):# 产生的类作为一个对象,传给元类
        def __init__(self,*args,**kwargs):
            print(*args)#Person () {'__module__': '__main__', '__qualname__': 'Person', 'school': 'hnsf', '__init__': <function Person.__init__ at 0x000001C81E7D7558>, 'score': <function Person.score at 0x000001C81E7D75E8>}
    
            print(**kwargs)
    
    metaclass = Mymeta#指定这个类生成的时候,用自己写的Mymeta这个元类
    class Person(metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self,name):
            self.name = name
        def score(self):
            print('ok')
    
    class Mymeta(type):
        def __init__(self, name, bases, dic):
            print(self)
            # print(name)# 类名,Person
            print(self.__name__)  # 类名,Person
            # print(bases)# 类的父类,(<class 'object'>,)
            print(self.__bases__)  # 类的父类,(<class 'object'>,)
            # print(dic)
            print(self.__dict__)  # 类的名称空间,{'__module__': '__main__', '__qualname__': 'Person', 'school': 'hnsf', '__init__': <function Person.__init__ at 0x000002D6F6337558>, 'score': <function Person.score at 0x000002D6F63375E8>}
    
    metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
    class Person(object, metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self, name):
            self.name = name
        def score(self):
            print('ok')
    

    练习一:

    对产生的类进行限制,控制类必须以sb开头

    class Mymeta(type):
        def __init__(self, name, bases, dic):
            if not name.startswith('sb'):
                raise Exception('类名没有以sb开口')
    
    metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
    class sbPerson(object, metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self, name):
            self.name = name
        def score(self):
            print('ok')
    

    练习二,类必须加注释

    类的名称空间中的key值'__doc__'代表注释

    class Mymeta(type):
        def __init__(self, name, bases, dic):
            print(self.__dict__["__doc__"])
    
    metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
    class sbPerson(object, metaclass=Mymeta):
        """
        我已经注释
        """
        school = 'hnsf'
        def __init__(self, name):
            self.name = name
        def score(self):
            print('ok')
    
    
    class Mymeta(type):
        def __init__(self, name, bases, dic):
            doc = self.__dict__["__doc__"]
            if not doc:
                raise Exception('你的类没有加注释')
            else:
                print(doc)
    
    metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
    class sbPerson(object, metaclass=Mymeta):
        """
        我已经注释
        """
        school = 'hnsf'
        def __init__(self, name):
            self.name = name
        def score(self):
            print('ok')
    
    
    
    
    

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

    • 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发

    • 控制类的调用过程,实际是在控制:对象的产生

      class Mymeta(type):
          def __call__(self, *args, **kwargs):
              print('ocean')
              return 95
      
      class Person(object,metaclass=Mymeta):
          school = 'nasf'
          def __init__(self,name):
              self.name = name
          def score(self):
              print('100')
      
      p = Person('chen')#ocean
      print(p)#ocean 95
      
      class Person():
          school = 'nasf'
          def __init__(self,name):
              self.name = name
          def score(self):
              print('100')
          def __call__(self, *args, **kwargs):
              print('ocean')
      p = Person('chen')#自动触发__init__的执行
      
      
      class Person():
          school = 'nasf'
          def __init__(self,name):
              self.name = name
          def score(self):
              print('100')
          def __call__(self, *args, **kwargs):
              print('ocean111')
      p = Person('chen')
      p()# 当元类没有自定义__call__的时候,而且自身定义了__call__的时候,那么对象加()会触发
      
      #先触发元类的__call__
      # 当自身和自定义的元类都写了__call__方法的时候,先触发元类的__call__,并且不会生成对象
      
      class Mymeta(type):
          def __call__(self, *args, **kwargs):
              print('ocean')
              # return 95
      #
      #
      # p = Person('chen')#ocean
      # print(p)#ocean 95
      class Person(metaclass=Mymeta):
          school = 'nasf'
          def __init__(self,name):
              self.name = name
          def score(self):
              print('100')
          def __call__(self, *args, **kwargs):
              print('ocean111')
      p = Person('chen')#不会生成p对象
      
      

      练习:给我吧对象中的所有属性都设置成私有的

      分析:

      class Mymeta(type):
          def __call__(self, *args, **kwargs):
              #实例化产生一个Person类的对象,借助__new__来产生,
              # 需要把类传过去,才能产生对象,只不过是空的
              obj = object.__new__(self)
      
      class Person(object,metaclass=Mymeta):
          school = 'hnsf'
          def __init__(self,name):
              self.name = name
      p = Person('name')
      print(p)#None
      
      class Mymeta(type):
          def __call__(self, *args, **kwargs):
              #实例化产生一个Person类的对象,借助__new__来产生,
              # 需要把类传过去,才能产生对象,只不过是空的
              obj = object.__new__(self)
              #调用__init__方法完成初始化
              #类来调用__init__方法,就是一个普通函数,有几个参数就传几个参数
              # self.__init__(obj,*args,**kwargs)
              #对象来调用__init__方法,对象的绑定方法,会把自身传过去
              obj.__init__(*args,**kwargs)
              print(obj)
              return obj
      
      class Person(object,metaclass=Mymeta):
          school = 'hnsf'
          def __init__(self,name):
              self.name = name
      p = Person('name')
      print(p)
      print(p.name)
      

      把对象的所有的属性变为私有

      class Mymeta(type):
          def __call__(self, *args, **kwargs):
              obj = object.__new__(self)
              obj.__init__(*args,**kwargs)
              print(obj.__dict__)#{'name': 'ocean'}
      
              obj.__dict__ ={'_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()}
              print(obj.__dict__)#{'_Person__name': 'ocean'}
      
              return obj
      
      
      class Person(object,metaclass=Mymeta):
          school = 'hnsf'
          def __init__(self,name):
              self.name = name
      
      p = Person(name='ocean')
      
      print(p.__dict__)#{'_Person__name': 'ocean'}
      
      

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

      1. 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错
      2. 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错
      class Mymeta(type):
          n=444
      
          def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
              obj=self.__new__(self)
              # print(self.__new__ is object.__new__) #True
              obj.__init__(*args, **kwargs)
              return obj
      
      
      class Bar(object):
          # n=333
          pass
      
          # def __new__(cls, *args, **kwargs):
          #     print('Bar.__new__')
      
      class Foo(Bar):
          # n=222
          pass
      
          # def __new__(cls, *args, **kwargs):
          #     print('Foo.__new__')
      
      class OldboyTeacher(Foo,metaclass=Mymeta):
          # n=111
      
          school='oldboy'
      
          def __init__(self,name,age):
              self.name=name
              self.age=age
      
          def say(self):
              print('%s says welcome to the oldboy to learn Python' %self.name)
      
      
          # def __new__(cls, *args, **kwargs):
          #     print('OldboyTeacher.__new__')
      
      
      o=OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
      print(OldboyTeacher.n)
      # print(o.n)
      
  • 相关阅读:
    SVG
    颜色的小纠结
    Tomcat 安装与配置
    编译java代码出现 错误: 需要class, interface或enum 提示
    Java 开发环境配置
    关于shortcut icon和icon
    转载文章CSS3的calc()使用
    小tip:CSS vw让overflow:auto页面滚动条出现时不跳动——张鑫旭
    CSS计数器(序列数字字符自动递增)详解———张鑫旭
    一起来看看JavaScript中==和===有何不同
  • 原文地址:https://www.cnblogs.com/SkyOceanchen/p/11454652.html
Copyright © 2020-2023  润新知