找回密码
 立即注册
首页 业界区 安全 Python元类

Python元类

尸酒岐 昨天 16:15
1、概念

在Python中,一切皆对象。你定义的类(即使是内置类型)本身也是一种对象。既然类是对象,那么它们也必然由某个“东西”创建。这个创建类的“东西”就是元类(Metaclass)。

  • 元类是创建类的类。普通类定义了实例对象的行为,而元类定义了类的行为
  • 默认情况下,Python中所有的类都是由内置元类type创建的
  1. class MyClass:
  2.     pass
  3. print(type(MyClass))  # 输出:<class 'type'>
  4. print(type(1))         # 输出:<class 'int'>
  5. print(type("hello"))   # 输出:<class 'str'>
复制代码
2、元类的工作原理:类创建过程

当你使用 class关键字定义一个类时,Python在幕后执行了以下几步,只要理解类本身就是元类的实例,就容易理解调用__new____init__的时机:

  • 收集类定义内容:解析类定义体,获取类名、基类和包含属性/方法的字典。
  • 确定元类:检查是否指定了自定义元类(通过metaclass关键字),若无则使用默认的type。
  • 执行元类的__new__方法:元类的__new__方法负责创建并返回这个类对象。这是干预类创建、修改类属性(如方法、类变量)的关键阶段。
  • 执行元类的__init__方法:元类的__init__方法负责对创建好的类对象进行初始化。
  • 将类对象绑定到类名:最终,创建的类对象被赋值给类定义头中的类名。
type函数除了可以查看对象的类型,还可以动态地创建类,其语法如下:
  1. MyClass = type('MyClass', (BaseClass,), {'attribute': value, 'method': my_method})
复制代码
3、定义自定义元类

自定义元类通常通过继承type并重写其__new____init__方法来实现,元类的__new____init__方法在类定义阶段就被调用了
3.1 基本元类结构
  1. class MyMeta(type):
  2.     """自定义元类,继承自 type"""
  3.    
  4.     def __new__(cls, name, bases, attrs):
  5.         """
  6.         在类创建时调用
  7.         - cls: 元类本身
  8.         - name: 要创建的类名
  9.         - bases: 父类元组
  10.         - attrs: 类属性字典
  11.         """
  12.         print(f"创建类: {name}")
  13.         print(f"父类: {bases}")
  14.         print(f"属性: {list(attrs.keys())}")
  15.         
  16.         # 必须调用 type.__new__ 来实际创建类
  17.         return super().__new__(cls, name, bases, attrs)
  18.    
  19.     def __init__(self, name, bases, attrs):
  20.         """类初始化时调用"""
  21.         super().__init__(name, bases, attrs)
  22.         print(f"初始化类: {name}")
  23. # 使用元类
  24. class MyClass(metaclass=MyMeta):
  25.     x = 42
  26.    
  27.     def method(self):
  28.         return "hello"
  29. # 输出:
  30. # 创建类: MyClass
  31. # 父类: ()
  32. # 属性: ['__module__', '__qualname__', 'x', 'method']
  33. # 初始化类: MyClass
复制代码
3.2 修改类定义

元类可以在类创建时修改其属性:
  1. class UpperCaseMeta(type):
  2.     """将类中所有非特殊属性名改为大写"""
  3.    
  4.     def __new__(cls, name, bases, attrs):
  5.         # 过滤出需要修改的属性
  6.         uppercase_attrs = {}
  7.         for attr_name, attr_value in attrs.items():
  8.             if not attr_name.startswith('__'):  # 不处理特殊方法
  9.                 uppercase_attrs[attr_name.upper()] = attr_value
  10.             else:
  11.                 uppercase_attrs[attr_name] = attr_value
  12.         
  13.         return super().__new__(cls, name, bases, uppercase_attrs)
  14. class MyClass(metaclass=UpperCaseMeta):
  15.     my_attr = "value"
  16.     another_attr = 42
  17.    
  18.     def my_method(self):
  19.         return "original"
  20. # 测试
  21. obj = MyClass()
  22. print(hasattr(obj, 'my_attr'))      # False
  23. print(hasattr(obj, 'MY_ATTR'))      # True
  24. print(hasattr(obj, 'my_method'))    # False  
  25. print(hasattr(obj, 'MY_METHOD'))    # True
复制代码
4、使用场景

4.1 单例模式实现

由于类是元类的对象,因此实例化对象就类似于普通对象直接加(),就会调用元类的__call__方法
  1. class SingletonMeta(type):
  2.     """单例元类"""
  3.     _instances = {}
  4.     def __call__(cls, *args, **kwargs):
  5.         if cls not in cls._instances:
  6.             cls._instances[cls] = super().__call__(*args, **kwargs)
  7.         return cls._instances[cls]
  8. class DatabaseConnection(metaclass=SingletonMeta):
  9.     def __init__(self, connection_string):
  10.         self.connection_string = connection_string
  11.         print(f"连接到数据库: {connection_string}")
  12.    
  13.     def query(self, sql):
  14.         return f"执行查询: {sql}"
  15. # 测试单例
  16. db1 = DatabaseConnection("mysql://localhost:3306/mydb")
  17. db2 = DatabaseConnection("mysql://localhost:3306/mydb")
  18. print(db1 is db2)  # True
  19. print(db1.query("SELECT * FROM users"))
复制代码
4.2 ORM(对象关系映射)框架
  1. class Field:
  2.     """字段基类"""
  3.     def __init__(self, field_type, required=True, default=None):
  4.         self.field_type = field_type
  5.         self.required = required
  6.         self.default = default
  7. class CharField(Field):
  8.     def __init__(self, max_length=255, **kwargs):
  9.         super().__init__(str, **kwargs)
  10.         self.max_length = max_length
  11. class IntegerField(Field):
  12.     def __init__(self, **kwargs):
  13.         super().__init__(int, **kwargs)
  14. class ModelMeta(type):
  15.     """ORM 元类,用于收集字段信息"""
  16.    
  17.     def __new__(cls, name, bases, attrs):
  18.         # 收集字段信息
  19.         fields = {}
  20.         for attr_name, attr_value in attrs.items():
  21.             if isinstance(attr_value, Field):
  22.                 fields[attr_name] = attr_value
  23.                 # 从类属性中移除字段,避免干扰实例属性
  24.                 attrs[attr_name] = None
  25.         
  26.         # 添加_fields属性保存字段信息
  27.         attrs['_fields'] = fields
  28.         
  29.         return super().__new__(cls, name, bases, attrs)
  30. class Model(metaclass=ModelMeta):
  31.     """模型基类"""
  32.    
  33.     def __init__(self, **kwargs):
  34.         for field_name, field in self._fields.items():
  35.             value = kwargs.get(field_name, field.default)
  36.             setattr(self, field_name, value)
  37.    
  38.     def validate(self):
  39.         """验证字段值"""
  40.         errors = []
  41.         for field_name, field in self._fields.items():
  42.             value = getattr(self, field_name)
  43.             if value is None and field.required:
  44.                 errors.append(f"{field_name} is required")
  45.             elif value is not None and not isinstance(value, field.field_type):
  46.                 errors.append(f"{field_name} must be {field.field_type.__name__}")
  47.         return errors
  48. # 使用元类创建模型,子类的创建会继承父类的元类
  49. class User(Model):
  50.     name = CharField(max_length=100)
  51.     age = IntegerField(required=False, default=0)
  52.     email = CharField(max_length=255)
  53. # 测试
  54. user = User(name="Alice", age=25, email="alice@example.com")
  55. print(user.name)        # Alice
  56. print(user.age)         # 25
  57. print(user.validate())  # []
  58. user2 = User(name="Bob")
  59. print(user2.validate())  # ['email is required']
复制代码
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册