Python教程39:元类(Metaclass)

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

元类是Python的高级特性,用于创建类的"类"。今天我们揭开元类的神秘面纱,理解Python对象系统的底层机制。

1. 什么是元类

一切皆对象

在Python中,类也是对象:

 1class MyClass:
 2    pass
 3
 4# 类是对象
 5print(type(MyClass))  # <class 'type'>
 6print(isinstance(MyClass, type))  # True
 7
 8# 类可以赋值、作为参数
 9AnotherName = MyClass
10def func(cls):
11    return cls()

元类(Metaclass)

  • 创建类的"类"
  • type是Python的默认元类
  • 类是元类的实例
1# 类是type的实例
2class MyClass:
3    pass
4
5print(type(MyClass))  # <class 'type'>
6
7# 等价于
8MyClass = type('MyClass', (), {})

type的三种用法

 1# 用法1:查看对象类型
 2print(type(5))  # <class 'int'>
 3
 4# 用法2:动态创建类
 5# type(name, bases, dict)
 6MyClass = type('MyClass', (), {'x': 5})
 7obj = MyClass()
 8print(obj.x)  # 5
 9
10# 用法3:作为元类(继承type)
11class MyMeta(type):
12    pass

2. 动态创建类

使用type动态创建类:

 1# 方式1:正常定义
 2class Dog:
 3    def bark(self):
 4        return "Woof!"
 5
 6# 方式2:用type创建(等价)
 7def bark(self):
 8    return "Woof!"
 9
10Dog = type('Dog', (), {'bark': bark})
11
12dog = Dog()
13print(dog.bark())  # Woof!

type()参数

  • name:类名
  • bases:父类元组
  • dict:类属性字典
 1# 带继承和属性
 2class Animal:
 3    def eat(self):
 4        return "Eating"
 5
 6Dog = type('Dog', 
 7           (Animal,),  # 继承Animal
 8           {
 9               'species': 'Canis',  # 类属性
10               'bark': lambda self: "Woof!"  # 方法
11           })
12
13dog = Dog()
14print(dog.eat())    # Eating(继承)
15print(dog.bark())   # Woof!
16print(dog.species)  # Canis

3. 自定义元类

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

 1class MyMeta(type):
 2    """自定义元类"""
 3    
 4    def __new__(cls, name, bases, attrs):
 5        """
 6        创建类时调用
 7        - cls: 元类本身
 8        - name: 类名
 9        - bases: 父类
10        - attrs: 属性字典
11        """
12        print(f"创建类:{name}")
13        
14        # 修改类属性
15        attrs['version'] = '1.0'
16        
17        # 调用父类创建类
18        return super().__new__(cls, name, bases, attrs)
19
20# 使用元类
21class MyClass(metaclass=MyMeta):
22    pass
23# 输出:创建类:MyClass
24
25print(MyClass.version)  # 1.0(自动添加)

元类的应用场景

 1class UpperAttrMeta(type):
 2    """
 3    元类:将所有属性名转为大写
 4    """
 5    def __new__(cls, name, bases, attrs):
 6        # 转换属性名为大写
 7        uppercase_attrs = {
 8            (key.upper() if not key.startswith('__') else key): value
 9            for key, value in attrs.items()
10        }
11        return super().__new__(cls, name, bases, uppercase_attrs)
12
13class MyClass(metaclass=UpperAttrMeta):
14    x = 10
15    y = 20
16    
17    def hello(self):
18        return "Hello"
19
20obj = MyClass()
21print(obj.X)  # 10(x变成了X)
22print(obj.Y)  # 20
23print(obj.HELLO())  # Hello(方法名也变了)

4. 实战示例

示例:单例模式元类

 1class SingletonMeta(type):
 2    """单例元类"""
 3    
 4    _instances = {}
 5    
 6    def __call__(cls, *args, **kwargs):
 7        """
 8        调用类创建实例时触发
 9        - 检查是否已有实例
10        - 有则返回,无则创建
11        """
12        if cls not in cls._instances:
13            instance = super().__call__(*args, **kwargs)
14            cls._instances[cls] = instance
15        return cls._instances[cls]
16
17class Database(metaclass=SingletonMeta):
18    def __init__(self, connection_string):
19        self.connection_string = connection_string
20
21# 测试
22db1 = Database("localhost")
23db2 = Database("otherhost")  # 参数被忽略
24
25print(db1 is db2)  # True(同一实例)
26print(db1.connection_string)  # localhost

示例:ORM字段验证

 1class Field:
 2    """字段描述符"""
 3    def __init__(self, field_type):
 4        self.field_type = field_type
 5    
 6    def __set_name__(self, owner, name):
 7        self.name = name
 8    
 9    def __get__(self, instance, owner):
10        if instance is None:
11            return self
12        return instance.__dict__.get(self.name)
13    
14    def __set__(self, instance, value):
15        if not isinstance(value, self.field_type):
16            raise TypeError(f"{self.name}必须是{self.field_type.__name__}")
17        instance.__dict__[self.name] = value
18
19class ModelMeta(type):
20    """ORM模型元类"""
21    def __new__(cls, name, bases, attrs):
22        # 收集所有Field
23        fields = {}
24        for key, value in attrs.items():
25            if isinstance(value, Field):
26                fields[key] = value
27        
28        attrs['_fields'] = fields
29        return super().__new__(cls, name, bases, attrs)
30
31class Model(metaclass=ModelMeta):
32    """ORM基类"""
33    pass
34
35class User(Model):
36    name = Field(str)
37    age = Field(int)
38    
39    def __init__(self, name, age):
40        self.name = name
41        self.age = age
42
43# 使用
44user = User("Alice", 25)
45print(user.name, user.age)
46# user.age = "abc"  # TypeError

5. new vs init 在元类中

 1class MyMeta(type):
 2    def __new__(cls, name, bases, attrs):
 3        """
 4        创建类对象
 5        - 在__init__之前调用
 6        - 必须返回类对象
 7        """
 8        print(f"__new__: 创建类{name}")
 9        return super().__new__(cls, name, bases, attrs)
10    
11    def __init__(cls, name, bases, attrs):
12        """
13        初始化类对象
14        - 在__new__之后调用
15        - 不返回值
16        """
17        print(f"__init__: 初始化类{name}")
18        super().__init__(name, bases, attrs)
19
20class MyClass(metaclass=MyMeta):
21    pass
22# 输出:
23# __new__: 创建类MyClass  
24# __init__: 初始化类MyClass

6. 何时使用元类

使用元类的场景

  • 自动注册类
  • API框架(Django ORM)
  • 代码注入
  • 接口检查
  • 日志和调试

不使用元类的场景(99%的情况):

  • 装饰器能解决
  • 类装饰器能解决
  • Mixin能解决
  • 继承能解决
1# 通常装饰器就够了
2def add_version(cls):
3    cls.version = '1.0'
4    return cls
5
6@add_version
7class MyClass:
8    pass

7. 小结

元类是Python的高级特性:

  • 元类:创建类的类,type是默认元类
  • 动态创建类:type(name, bases, dict)
  • 自定义元类:继承type
  • 应用:单例、ORM、框架
  • 警告:99%情况不需要元类

除非开发框架,否则优先考虑装饰器、Mixin等更简单的方案。


练习题

  1. 用type动态创建一个带方法的类
  2. 实现一个元类,自动为类添加__str__方法
  3. 创建一个注册元类,自动注册所有子类

本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读