首页 / Python3 入门教程 / 枚举类与元类入门

Python3 入门教程

枚举类与元类入门

本教程共 70 篇 · 第 38 篇 · 更新于 2026-07-22 · 约 5 分钟阅读

PythonPython3 入门教程枚举类Enum元类type()

38. 枚举类与元类入门

本节目标:学会用 Enum 定义枚举,理解 type() 动态创建类的机制,对元类(metaclass)有初步认知。

这一章进入 OOP 的深水区。枚举类和元类都不是每天写业务代码必用的东西,但它们在框架设计、代码生成、状态管理等场景下非常有价值。

枚举类:Enum

状态值用整数或字符串表示,容易写错:

# 不好的写法
RED = 1
GREEN = 2
BLUE = 3

color = 4  # 写错了,但程序不会报错

Python 3.4+ 提供了 enum 模块,让你定义语义清晰的枚举:

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

print(Color.RED)       # Color.RED
print(Color.RED.name)  # RED
print(Color.RED.value) # 1

Color.RED 是一个枚举成员,有 namevalue 两个属性。它不等于整数 1

print(Color.RED == 1)       # False
print(Color.RED == Color.RED)  # True
print(Color(1) == Color.RED)   # True
Tip

枚举成员是单例对象。同一个枚举值,无论怎么获取,得到的都是同一个对象。Color(1)Color.REDColor['RED'] 三者等价。

枚举的遍历和比较

for c in Color:
    print(c.name, c.value)

# RED 1
# GREEN 2
# BLUE 3

枚举成员之间可以用 is 比较,也可以用 ==。但不能做大小比较(除非用 IntEnum)。

自动赋值:auto()

如果枚举值本身不重要,可以用 auto() 自动分配:

from enum import Enum, auto

class Status(Enum):
    PENDING = auto()
    RUNNING = auto()
    DONE = auto()

print(Status.PENDING.value)  # 1
print(Status.RUNNING.value)  # 2

IntEnum:值等于整数的枚举

有时候你需要枚举成员直接参与整数运算,比如跟数据库里的状态码对接。用 IntEnum

from enum import IntEnum

class Priority(IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

print(Priority.LOW + 1)        # 2
print(Priority(2))             # Priority.MEDIUM
Warning

IntEnum 的成员等于整数,所以 Priority.LOW == 1 返回 True。这会弱化类型安全,只在确实需要和整数混用的场景下使用。

枚举的典型用法

枚举最适合表示「有限且固定的状态集合」:

from enum import Enum, auto

class OrderState(Enum):
    CREATED = auto()
    PAID = auto()
    SHIPPED = auto()
    DELIVERED = auto()
    CANCELLED = auto()

def handle_order(state: OrderState):
    if state == OrderState.PAID:
        print("通知仓库发货")
    elif state == OrderState.SHIPPED:
        print("发送物流信息给用户")
    else:
        print("无需处理")

handle_order(OrderState.PAID)

用枚举代替魔法数字,代码的可读性和可维护性都会提升。

type():类的类

我们一直说「类是创建实例的模板」。那类本身是什么创建的?答案是 type

class Foo:
    pass

print(type(Foo))  # <class 'type'>

type 是所有类的类,也叫「元类(metaclass)」。

更有趣的是,type 还可以当函数用,动态创建类:

def greet(self):
    print(f"Hello, I'm {self.name}")

Person = type('Person', (), {'name': '匿名', 'greet': greet})

p = Person()
p.greet()  # Hello, I'm 匿名

type(name, bases, dict) 的三个参数:

  • name:类名(字符串)。
  • bases:父类元组。
  • dict:类属性和方法的字典。

这等价于:

class Person:
    name = '匿名'
    def greet(self):
        print(f"Hello, I'm {self.name}")
Note

动态创建类在框架开发中很常见。比如 ORM(对象关系映射)库,会根据数据库表结构自动生成对应的 Python 类。背后用的就是 type() 或元类。

元类入门

元类是「创建类的类」。默认的元类是 type。你可以自定义元类,在类创建时插入自己的逻辑。

自定义元类需要继承 type,并重写 __new__ 方法:

class AutoReprMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)

        # 自动给没有 __repr__ 的类添加一个
        if '__repr__' not in namespace:
            def auto_repr(self):
                attrs = ', '.join(f"{k}={v!r}" for k, v in self.__dict__.items())
                return f"{name}({attrs})"
            cls.__repr__ = auto_repr

        return cls

class Point(metaclass=AutoReprMeta):
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2)
print(p)  # Point(x=1, y=2)

metaclass=AutoReprMeta 告诉 Python:创建 Point 类时,不要用默认的 type,而用我自定义的元类。元类的 __new__ 在类对象被创建时执行,此时实例还不存在。

Warning

元类是 Python 最复杂的特性之一。日常开发几乎用不到,但在写框架、ORM、API 生成器时,它能大幅减少重复代码。如果你现在觉得晕,完全正常。记住两点就行:1) 类是 type 的实例;2) 自定义元类可以干预类的创建过程。

一个小实战:用元类做接口检查

假设你要求所有「服务类」都必须实现 run 方法。可以用元类在类定义时就检查:

class ServiceMeta(type):
    def __new__(mcs, name, bases, namespace):
        if bases and 'run' not in namespace:
            raise TypeError(f"{name} 必须实现 run 方法")
        return super().__new__(mcs, name, bases, namespace)

class BaseService(metaclass=ServiceMeta):
    pass

class EmailService(BaseService):
    def run(self):
        print("发送邮件")

# class BadService(BaseService):
#     pass  # TypeError: BadService 必须实现 run 方法

这比运行时才报错要好得多。问题在定义阶段就暴露,符合「fail fast」原则。

小结

  • Enum 让状态值类型安全、语义清晰,替代魔法数字。
  • type(name, bases, dict) 可以动态创建类。
  • 元类是创建类的类,默认是 type
  • 自定义元类通过 metaclass= 参数指定,能在类创建时执行额外逻辑。

枚举是日常开发就能用到的工具,元类则是进入框架开发领域的钥匙。两者都体现了 Python 的哲学:一切皆对象,连类本身也不例外。


来源:参考了 runoob「Python3 面向对象」、liaoxuefeng「使用枚举类」和「使用元类」、w3cschool「Python3 面向对象」等,改写后所得。