• 面向对象之元类


    1. 元类

    1.1 什么是元类

    • 在python中,一切皆对象,那么我们用class关键字定义的类也是一个对象,而负责产生该对象的类称之为元类

    1.2 为什么用元类

    • 元类是负责产生类的,所以我们可以通过元类控制类的产生过程,把一些不符合的筛选掉,同时还可以通过元类里的__call__方法控制对象的产生

    1.3 类的创建

    1.3.1 正常定义一个类

    # 定义一个类
    class Foo(object):  # python3中默认继承object类
        count = 0
        
        def __init__(self,name,age):
            self.name = name
            self.age = age
            
        @property    
        def func(self):
            print('from func')
            
    f1 = Foo('hades',27)
    print(f1.__dict__)
    f1.func
    
    {'name': 'hades', 'age': 27}
    from func
    

    1.3.2 内置函数exec

    exec会把字符串按照python语法解析成代码,把解析完的代码放入字典中

    • 语法:exec(str:-->符合Python语法规范的字符串, {}:-->Python中的内置函数, 变量名:-->dict)
    s =  '''
    count = 0
        
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    def func(self):
        print('from func')
    '''
    dic = dict()
    exec(s,{},dic)  
    print(dic)
    
    {'count': 0, '__init__': <function __init__ at 0x0000023CB5544C80>, 'func': <function func at 0x0000023CB55FD378>}
    

    1.3.3 type创建类

    1.3.3.1 类的三大组成

    1. 类名
    2. 基类
    3. 类的名称空间
    # 1. 类名
    class_name = 'Foo'
    
    # 2. 类体代码(需要按照python语法规则)
    class_body = '''
    count = 0
        
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
    @property
    def func(self):
        print('from func')
    '''
    
    # 3. 基类(父类)
    class_bases =(object,)  # 必须加逗号,形成一个元组
    
    class_dict = dict()
    exec(class_body, {}, class_dict)   # 产生类体名称空间
    print(class_dict)
    
    {'count': 0, '__init__': <function __init__ at 0x0000023CB56842F0>, 'func': <property object at 0x0000023CB56773B8>}
    

    1.3.3.2 type关键字生成类

    • 语法:type(类名:-->str,基类:-->tuple, 类的名称空间:-->dict)
    Foo1 = type(class_name, class_bases, class_dict)
    print(Foo1)
    
    <class '__main__.Foo'>
    
    f2 = Foo1('hades', 27)
    print(f2.__dict__)
    f2.func
    
    {'name': 'hades', 'age': 27}
    from func
    

    这样我们就生成了一个类

    1.3.4 自定义元类控制类的创建

    • 使用自定义个元类
    class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个很普通的类
        def __init__(self, class_name, class_bases, class_dict):
            print(class_name)
            print(class_bases)
            print(class_dict)
            super().__init__( class_name, class_bases, class_dict)  # 使用父类功能生成一个类对象
            print('创建类成功了')
    
    • 分析用class自定义类的运行原理(而非元类的的运行原理):

      1.拿到一个字符串格式的类名class_name = 'Foo'

      2.拿到一个类的基类们class_bases = (obejct,)

      3.执行类体代码,拿到一个类的名称空间class_dict={...}

      4.调用Foo1 = type(class_name, class_bases, class_dict)

    class Foo(metaclass=Mymeta):  # python3中默认继承object类
        count = 0
        
        def __init__(self,name,age):
            self.name = name
            self.age = age
            
        @property    
        def func(self):
            print(f'name "{self.name}" from func')
    
    Foo
    ()
    {'__module__': '__main__', '__qualname__': 'Foo', 'count': 0, '__init__': <function Foo.__init__ at 0x0000023CB5684D08>, 'func': <property object at 0x0000023CB5690368>}
    创建类成功了
    
    f1 = Foo('Bonnie',16)
    f1.func
    
    name "Bonnie" from func
    

    1.4 应用

    • 自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程

    • 我们可以控制类必须有文档,类名首字母要大写,可以使用如下的方式实现

    class Mymeta(type): 
        def __init__(self, class_name, class_bases, class_dict):
            if not class_name.istitle():
                raise TypeError('类名首字母要进行大写')
                
            if (not class_dict.get('__doc__')) or len(class_dict['__doc__']) == 0:
                raise TypeError("必须有文档注释")
                
            super().__init__( class_name, class_bases, class_dict)
            
    
    # 类名首字母没有大写
    try:
        class foo(metaclass=Mymeta):
            def __init__(self,name,age):
                self.name = name
                self.age = age
                
    except Exception as e:
        print(e)
    
    类名首字母要进行大写
    
    # 没有注释
    try:
        class Foo(metaclass=Mymeta):
            def __init__(self,name,age):
                self.name = name
                self.age = age
                
    except Exception as e:
        print(e)
    
    必须有文档注释
    
    try:
        class Foo(metaclass=Mymeta):
            """测试文件"""
            def __init__(self,name,age):
                self.name = name
                self.age = age
                
        f1 = Foo('hades',20)
        print(f1.__dict__)   # {'name': 'hades', 'age': 20}
                
    except Exception as e:
        print(e)
    
    {'name': 'hades', 'age': 20}
    

    1.5 自定义元类控制类的实例化

    可以通过__call__方法进行实现

    class Mymeta(type): 
        def __init__(self, class_name, class_bases, class_dict):
            if not class_name.istitle():
                raise TypeError('类名首字母要进行大写')
                
            if (not class_dict.get('__doc__')) or len(class_dict['__doc__']) == 0:
                raise TypeError("必须有文档注释")
                
            super().__init__( class_name, class_bases, class_dict)
            
        def __call__(self,*args,**kwargs):
            print(self)
            print(args)
            print(kwargs)
            
            obj = self.__new__(self)
            self.__init__(obj,*args,**kwargs)
            print(obj)
            return obj
    
    class Foo(metaclass=Mymeta):
        """测试文件"""
        def __init__(self,name,age):
            self.name = name
            self.age = age
            
    f1 = Foo('Bonnie',18)
    print(f1)
    print(f1.__dict__)
    
    <class '__main__.Foo'>
    ('Bonnie', 18)
    {}
    <__main__.Foo object at 0x0000023CB56B4048>
    <__main__.Foo object at 0x0000023CB56B4048>
    {'name': 'Bonnie', 'age': 18}
    
    • 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制

    • 分析:调用Pepole的目的

      1. 先造出一个People的空对象

      2. 为该对空对象初始化独有的属性

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

    1.6 自定制元类后类的继承顺序

    • 先对象本身-->类-->父类-->父类的父类-->object-->自己定制的元类-->type
  • 相关阅读:
    大数据应用期末总评
    分布式文件系统HDFS 练习
    安装Hadoop
    爬虫综合大作业
    爬取全部的校园新闻
    理解爬虫原理
    中文词频统计与词云生成
    复合数据类型,英文词频统计
    字符串操作、文件操作
    了解大数据的特点、来源与数据呈现方式
  • 原文地址:https://www.cnblogs.com/Hades123/p/11068350.html
Copyright © 2020-2023  润新知