zoukankan      html  css  js  c++  java
  • 元类

    一:元类介绍

    “元类就是深度的魔法,99%的用户应该根本不必为此操心。

    如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。

    那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。”

    —— Python界的领袖 Tim Peters


    一切都源自一句话:在Python中,一切皆对象

    元类:类他爹

    定义类,可以控制对象的产生

    元类,可以控制类的产生

    # 元类 ==> People类 ==> obj
    class People(object):
        school = 'Tinghua'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    
    
    obj = People('xxq', 18)
    # 调用People类 ==> 对象obj
    # 调用元类 ==> People类
    
    # 查看类(类型)
    print(type(obj))  # <class '__main__.People'>
    print(type(People))  # <class 'type'>   # 默认的元类是:type
    
    
    

    结论:

    默认的元类是:type,默认情况下,我们用class关键字定义的类 都是由type产生的

    二:class关键字 底层做了哪些事?

    1.先拿到1个类名

    class_name = 'People'
    

    2.拿到 类的父类

    class_bases = (object,)
    

    3.再拿到类的名称空间(运行类体代码得到的)

    class_dic = {}
    class_body = '''
        school = 'Tinghua'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    '''
    exec(class_body, {}, class_dic)
    # print(class_dic)
    

    补充:exec的使用

    dic = {}    # 空字典
    s = '''
    a = 1
    b = 2
    c = 3
    '''
    
    exec(s, {}, dic)
    print(dic)
    

    4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)

    People = type(class_name, class_bases, class_dic)
    

    5.完整思路

    # 1.先拿到1个类名
    class_name = 'People'
    
    # 2.拿到 类的父类
    class_bases = (object,)
    
    # 3.再拿到类的名称空间(运行类体代码得到的)
    class_dic = {}
    class_body = '''
        school = 'Tinghua'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    '''
    exec(class_body, {}, class_dic)
    # print(class_dic)
    
    # 4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)
    People = type(class_name, class_bases, class_dic)
    

    三:自定义元类

    1.先拿到1个类名:'People'

    2.拿到 类的父类:(object,)

    3.再拿到类的名称空间(运行类体代码得到的){...}

    4.调用元类(传入类的3大要素:类名、基类、类的名称空间) 得到1个元类的对象,然后将元类的变量赋值给People(这个People就是我们用class自定义的类)

    People = Mymeta('People', (object,), {...})

    1.自定义元类

    只有继承了type的类 才是自定义的元类

    可以通过自定义的元类来规范类的产生

    import re
    
    
    class Mymeta(type):  # 只有继承了type的类 才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            print(self)  # 类
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            # 规范类名
            if not re.match('[A-Z]', class_name):
                raise BaseException('类名必须用驼峰体')
    
            # 至少继承一个类
            if len(class_bases) == 0:
                raise BaseException('至少继承一个类')
    
            # 文档注释
            # print('文档注释:', class_dic.get('__doc__'))
            doc = class_dic.get('__doc__')
            if not (doc and len(doc.strip()) > 0):
                raise BaseException('必须要有文档注释,并且注释内容不能为空')
    
    
    # People = Mymeta('People', (object,), {...})
    class People(object, metaclass=Mymeta):
        '''
        这是我的文档注释
        '''
        school = 'Tinghua'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    

    2.探究是否可调用

    ①自定义的类实例化的对象和其他int、list都不可调用

    class Foo:
        pass
    
    
    obj1 = Foo()
    
    obj2 = int(10)
    
    obj3 = list([1, 2, 3])
    
    
    # obj1()
    # TypeError: 'Foo' object is not callable
    
    # obj2()
    # TypeError: 'int' object is not callable
    
    # obj3()
    # TypeError: 'list' object is not callable
    

    ②自定义的类加上__call__方法后,实例化的对象,是可以直接通过加()被调用的

    调用对象 其实就是在调用对象类中 定义的绑定方法:__call__

    class Foo:
        def __call__(self, *args, **kwargs):
            print('=========')
            print(self)
            print(args)
            print(kwargs)
    
    
    obj1 = Foo()
    obj1(1, 2, 3, a=1, b=2) # 调用对象 其实就是在调用对象类中 定义的绑定方法:__call__
    # (1, 2, 3)
    # {'a': 1, 'b': 2}
    

    3.自定义元类 控制类的调用

    import re
    
    
    class Mymeta(type):  # 只有继承了type的类 才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            print(self)  # 类
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            # 规范类名
            if not re.match('[A-Z]', class_name):
                raise BaseException('类名必须用驼峰体')
    
            # 至少继承一个类
            if len(class_bases) == 0:
                raise BaseException('至少继承一个类')
    
            # 文档注释
            # print('文档注释:', class_dic.get('__doc__'))
            doc = class_dic.get('__doc__')
            if not (doc and len(doc.strip()) > 0):
                raise BaseException('必须要有文档注释,并且注释内容不能为空')
    
        def __call__(self, *args, **kwargs):
            # 1.先创建一个空对象
            # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
            # 3.将初始化好的对象 赋值给变量名obj
            return 123
    
    
    # People = Mymeta('People', (object,), {...})
    class People(object, metaclass=Mymeta):
        '''
        这是我的文档注释
        '''
        school = 'Tinghua'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    
    
    people_obj = People()  # <class '__main__.People'>
    
    print(people_obj)  # 123
    

    4.自定义元类 控制类的调用 - 进阶

    import re
    
    
    class Mymeta(type):  # 只有继承了type的类 才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            print(self)  # 类
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            # 规范类名
            if not re.match('[A-Z]', class_name):
                raise BaseException('类名必须用驼峰体')
    
            # 至少继承一个类
            if len(class_bases) == 0:
                raise BaseException('至少继承一个类')
    
            # 文档注释
            # print('文档注释:', class_dic.get('__doc__'))
            doc = class_dic.get('__doc__')
            if not (doc and len(doc.strip()) > 0):
                raise BaseException('必须要有文档注释,并且注释内容不能为空')
    
        # people_obj = People() 触发了下面的__call__
        def __call__(self, *args, **kwargs):
            # 1.先创建一个人的空对象
            people_obj = object.__new__(self)
            # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
            # People.__init__
            self.__init__(people_obj, *args, **kwargs)
            # 3.将初始化好的人对象 赋值给变量名people_obj
            return people_obj
    
    
    # People = Mymeta('People', (object,), {...})
    class People(object, metaclass=Mymeta):
        '''
        这是我的文档注释
        '''
        school = 'Tinghua'
    
        #      people_obj,'xxq',18
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    
    
    people_obj = People('xxq', 18)  # <class '__main__.People'>
    
    print(people_obj)  # <__main__.People object at 0x00000200F400DD90>
    
    print(people_obj.name)  # xxq
    print(people_obj.age)  # 18
    print(people_obj.say)  # <bound method People.say of <__main__.People object at 0x000001E2AE41DD90>>
    
    # 调用People类的时候,发生了什么
    # 1.先创建一个人(people)的空对象
    # 2.调用类的__init__方法,将空独享连同括号内的参数 一同传给__init__
    # 3.将初始化好的人对象 赋值给变量名people_obj
    

    5.自定义元类 将类的属性隐藏

    import re
    
    
    class Mymeta(type):  # 只有继承了type的类 才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            print(self)  # 类
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            # 规范类名
            if not re.match('[A-Z]', class_name):
                raise BaseException('类名必须用驼峰体')
    
            # 至少继承一个类
            if len(class_bases) == 0:
                raise BaseException('至少继承一个类')
    
            # 文档注释
            # print('文档注释:', class_dic.get('__doc__'))
            doc = class_dic.get('__doc__')
            if not (doc and len(doc.strip()) > 0):
                raise BaseException('必须要有文档注释,并且注释内容不能为空')
    
        # people_obj = People() 触发了下面的__call__
        def __call__(self, *args, **kwargs):
            # 1.先创建一个人的空对象
            people_obj = object.__new__(self)
            # 2.调用类的__init__方法,将空对象连同括号内的参数 一同传给__init__
            # People.__init__
            self.__init__(people_obj, *args, **kwargs)
            print(people_obj.__dict__)
            people_obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in people_obj.__dict__.items()}
            # 3.将初始化好的人对象 赋值给变量名people_obj
            return people_obj
    
    
    # People = Mymeta('People', (object,), {...})
    class People(object, metaclass=Mymeta):
        '''
        这是我的文档注释
        '''
        school = 'Tinghua'
    
        #      people_obj,'xxq',18
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'Hello {self.name}!')
    
    
    people_obj = People('xxq', 18)
    # <class '__main__.People'>
    # {'name': 'xxq', 'age': 18}
    
    print(people_obj.__dict__)
    # {'_People__name': 'xxq', '_People__age': 18}
    
  • 相关阅读:
    spring 循环依赖问题
    spring data jpa 关键字 命名
    mongodb 添加字段并设置默认值
    java mongoTemplate的group统计
    java8 从对象集合中取出某个字段的集合
    springboot12-zuul
    springboot11-01-security入门
    springboot项目怎么部署到外部tomcat
    springboot10-springcloud-eureka 服务注册与发现,负载均衡客户端(ribbon,feign)调用
    UML
  • 原文地址:https://www.cnblogs.com/xuexianqi/p/13523357.html
Copyright © 2011-2022 走看看