• Python3 元类


    元类

    exec方法

    • exec()方法可以执行字符串形式的python代码块
    • 使用方法exec(object, global=None, local=None)
      • object: 字符串类型的python代码块
      • global: 代表全局名称空间, 必须是字典, 默认为None, 如传参则表明该代码块在全局名称空间中运行
      • local: 代表局部名称空间, 可以是任何映射, 默认为None, 如传参则表明该代码块在局部空间中运行
    code = '''
    x = 0
    sum = x + y + z
    print(sum)
    '''
    y = 1
    z = 2
    
    global_dict = {'y': 2, 'z': 3}
    
    local_dict = {'y': 3, 'z': 4}
    
    exec(code)
    '''
    y = 1
    z = 2
    x = 0
    sum = x + y + z
    print(sum)
    '''
    
    exec(code, global_dict)
    '''
    相当于
    y = 2
    z = 3
    x = 0
    sum = x + y + z
    print(sum)
    '''
    
    exec(code, global_dict, local_dict)
    '''
    相当于
    y = 2
    z = 3
    def exec_func():
    	y = 3
    	z = 4
    	x = 0
        sum = x + y + z
        print(sum)
        
    exec_func()
    '''
    
    '''
    3
    5
    7
    '''
    

    type元类

    • 类本身也是对象, 是通过实例化元类得到的对象, 元类是类的类
    • python默认的元类是type, 也可以自定义元类来实现控制类的创建
    • 通过type元类来创建类 MyClass = type(class_name, class_bases, class_dict)
      • class_name 类名
      • class_bases 父类, 以元祖形式传入
      • class_dict 类的属性, 以字典的形式传入 (类的名称空间)
    # 用type元类来创建一个Chinese类
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age
    
    class_name = 'Chinese'
    class_bases = (object,)
    class_dict = {'country': 'China', '__init__': __init__}
    
    Chinese = type(class_name, class_bases, class_dict)
    
    c1 = Chinese('bigb', 'male', 18)
    
    print(c1.name)  # bigb
    print(c1.country)   # China
    
    

    自定义元类

    自定义元类控制类的创建

    • 我们可以通过自定义元类来实现控制类的创建过程
    • 自定义的元类必须继承type, 并且覆盖type的__init__的方法
    '''
    通过自定义元类来实现:
    1. 类名首字母必须大写
    2. 类中必须有文档注释
    
    '''
    
    class MyMeta(type):
    
        def __init__(self, class_name, class_bases, class_dict):
            print(class_name)  # chinese
            print(class_bases) # (<class 'object'>,)
            print(class_dict)  # {'__module__': '__main__', '__qualname__': 'chinese', 'country': 'China', '__init__': <function chinese.__init__ at 0x0000000009FBFD90>, 'kongfu': <function chinese.kongfu at 0x0000000009FBFE18>}
    
            # 类名首字母必须大写
            if not class_name.istitle():
                raise TypeError('类的首字母必须大写!')
    
            # 类中必须有注释
            if not class_dict.get('__doc__'):
                raise TypeError('类中必须有文档注释!')
    
            # 调用type中的__init__方法初始化对象
            super().__init__(class_name, class_bases, class_dict)
    
    
    class chinese(object, metaclass=MyMeta):  # foo = MyMeta('foo', (object, ) {...})
        country = 'China'
    
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
        def kongfu(self):
            print('降龙十八掌!')
            
    '''
        raise TypeError('类的首字母必须大写!')
    TypeError: 类的首字母必须大写!
    '''
    
    
    # 将类名大写, 再运行
    class Chinese(object, metaclass=MyMeta):  # foo = MyMeta('foo', (object, ) {...})
        country = 'China'
    
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
        def kongfu(self):
            print('降龙十八掌!')
            
            
    '''
        raise TypeError('类中必须有文档注释!')
    TypeError: 类中必须有文档注释!
    '''
    
    

    自定义元类控制类的调用

    • 调用一个对象时, 会触发对象的类当中的 __call__ 方法
    • 类本身也是个对象, 因此在调用类实例化对象的时候, 就会触发元类当中的__call__ 方法
    class MyMeta(type):
        def __call__(self, *args, **kwargs):
            # 产生一个空对象
            obj = self.__new__(self)  # self是类对象
    
            # 初始化空对象
            self.__init__(obj, *args, **kwargs)
    
            # 返回初始化好的对象
            return obj
    
    
    class Chinese(object, metaclass=MyMeta):  # foo = MyMeta('foo', (object, ) {...})
        country = 'China'
    
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
        def kongfu(self):
            print('降龙十八掌!')
    
    
    # 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法
    c1 = Chinese('bigb', 'male', 18)
    
    print(c1.name)
    
    '''
    1. __call__中的__new__生成了一个空对象
    2. __call__中的__init__初始化这个空对象
    3. __call__返回了这个对象,并赋值给了c1
    '''
    
    • 现在 我们可以在此基础上通过修改 __call__ 的逻辑从而控制类的调用过程
    # 通过元类让Chinese类实例化出来的对象的属性变为私有属性
    class MyMeta(type):
        def __call__(self, *args, **kwargs):
            # 产生一个空对象
            obj = self.__new__(self)  # self是类对象
    
            # 初始化空对象
            self.__init__(obj, *args, **kwargs)
            
            # 将对象的属性变成私有属性(对象._类__属性名)
            obj.__dict__ = {f'_{self.__name__}__{k}': v for k, v in obj.__dict__.items()}
            
            # 返回初始化好的对象
            return obj
    
    
    class Chinese(object, metaclass=MyMeta):  # foo = MyMeta('foo', (object, ) {...})
        country = 'China'
    
        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age
    
        def kongfu(self):
            print('降龙十八掌!')
    
    
    # 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法
    c1 = Chinese('bigb', 'male', 18)
    
    print(c1._Chinese__name)  # bigb
    
  • 相关阅读:
    javascript中new Date()的浏览器兼容性问题
    js 时间格式化
    HTML5 JS实现搜索匹配功能
    PHP中preg_match正则匹配的/u /i /s是什么意思
    微信开放接口获取用户昵称保存到MySQL中为空白
    linux下源码安装软件
    格式化MYSQL时间戳函数FROM_UNIXTIME
    Zabbix-proxy安装部署
    使用ss命令对tcp连接数和状态的监控性能优化
    Zabbix使用netstat监控会话
  • 原文地址:https://www.cnblogs.com/bigb/p/11796023.html
Copyright © 2020-2023  润新知