Python3 元类

元类

exec方法#

  • exec()方法可以执行字符串形式的python代码块
  • 使用方法exec(object, global=None, local=None)
    • object: 字符串类型的python代码块
    • global: 代表全局名称空间, 必须是字典, 默认为None, 如传参则表明该代码块在全局名称空间中运行
    • local: 代表局部名称空间, 可以是任何映射, 默认为None, 如传参则表明该代码块在局部空间中运行
Copy
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 类的属性, 以字典的形式传入 (类的名称空间)
Copy
# 用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__的方法
Copy
''' 通过自定义元类来实现: 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__ 方法
Copy
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__ 的逻辑从而控制类的调用过程
Copy
# 通过元类让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
posted @   MrBigB  阅读(523)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
CONTENTS