第6章 枚举与常量
C++ 的枚举类型和常量在 Python 中有直接的对应物。本章将介绍如何将枚举导出为 Python 对象,以及如何暴露模块级常量。
6.1 枚举绑定基础
Section titled “6.1 枚举绑定基础”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
// C++ 枚举类型enum class Color { Red = 1, Green = 2, Blue = 3};
enum class Status { Pending = 0, Success = 1, Error = 2};
PYBIND11_MODULE(enum_basic, m) { // 使用 py::enum_<EnumType> 绑定枚举 py::enum_<Color>(m, "Color", py::arithmetic(), "颜色枚举") .value("RED", Color::Red, "红色") .value("GREEN", Color::Green, "绿色") .value("BLUE", Color::Blue, "蓝色") // 导出到 Python .export_values();
py::enum_<Status>(m, "Status", py::arithmetic(), "状态枚举") .value("PENDING", Status::Pending, "待处理") .value("SUCCESS", Status::Success, "成功") .value("ERROR", Status::Error, "错误") .export_values();}Python 使用
Section titled “Python 使用”import enum_basic as eb
print(eb.Color.RED) # <Color.RED: 1>print(eb.Color.GREEN) # <Color.GREEN: 2>print(eb.Color.BLUE) # <Color.BLUE: 3>
print(eb.Status.PENDING) # <Status.PENDING: 0>print(eb.Status.SUCCESS) # <Status.SUCCESS: 1>
c = eb.Color.REDif c == eb.Color.RED: print("Color is red") # Color is red
color_val = int(eb.Color.BLUE)print(color_val) # 3
c1 = eb.Color.REDc2 = eb.Color.REDprint(c1 is c2) # Truepy::enum_<Color>(m, "Color", py::arithmetic(), "docstring") .value("RED", Color::Red, "RED value") .export_values();| 表达式 | 含义 |
|---|---|
py::enum_<Color> | 模板参数是枚举类型 |
py::arithmetic() | 启用算术运算(+、- 等),可用于标志位枚举 |
.value("NAME", Color::Red, "doc") | 导出枚举值 |
.export_values() | 将枚举值导出到模块级别(可以直接用 Color.RED) |
6.2 枚举导出方式
Section titled “6.2 枚举导出方式”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
enum class LogLevel { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3};
PYBIND11_MODULE(enum_export, m) { // 方式1: 在模块级别导出枚举值(推荐) // .export_values() 会把枚举成员导出到模块 py::enum_<LogLevel>(m, "LogLevel", py::arithmetic(), "日志级别") .value("DEBUG", LogLevel::DEBUG) .value("INFO", LogLevel::INFO) .value("WARNING", LogLevel::WARNING) .value("ERROR", LogLevel::ERROR) .export_values();
// 方式2: 保留在枚举类内部 // 去掉 .export_values() 后,访问方式是 LogLevel.DEBUG // 注意:这需要额外定义一个包装类 py::enum_<LogLevel>(m, "LogLevelNoExport", py::arithmetic()) .value("DEBUG", LogLevel::DEBUG) .value("INFO", LogLevel::INFO) .value("WARNING", LogLevel::WARNING) .value("ERROR", LogLevel::ERROR); // 在 Python 中需要通过 LogLevelNoExport.DEBUG 访问}Python 使用
Section titled “Python 使用”import enum_export as ee
print(ee.LogLevel.DEBUG) # <LogLevel.DEBUG: 0>print(ee.LogLevel.INFO) # <LogLevel.INFO: 1>export_values 的作用
Section titled “export_values 的作用”// 没有 .export_values()// 有 .export_values()// Python: module.DEBUG 或 module.LogLevel.DEBUG 都可以建议: 大多数情况下使用
.export_values(),这样与 Python 的习惯一致。
6.3 枚举比较操作
Section titled “6.3 枚举比较操作”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
// 标志位枚举(使用 py::arithmetic())enum class Permissions { NONE = 0, READ = 1, WRITE = 2, EXECUTE = 4, ALL = 7};
// 普通枚举enum class Priority { LOW = 1, MEDIUM = 2, HIGH = 3, CRITICAL = 4};
PYBIND11_MODULE(enum_compare, m) { // 标志位枚举(需要 arithmetic 支持) py::enum_<Permissions>(m, "Permissions", py::arithmetic(), "权限标志") .value("NONE", Permissions::NONE) .value("READ", Permissions::READ) .value("WRITE", Permissions::WRITE) .value("EXECUTE", Permissions::EXECUTE) .value("ALL", Permissions::ALL) .export_values();
py::enum_<Priority>(m, "Priority", "优先级") .value("LOW", Priority::LOW) .value("MEDIUM", Priority::MEDIUM) .value("HIGH", Priority::HIGH) .value("CRITICAL", Priority::CRITICAL) .export_values();}Python 使用
Section titled “Python 使用”import enum_compare as ec
p1 = ec.Priority.HIGHp2 = ec.Priority.HIGHp3 = ec.Priority.MEDIUM
print(p1 == p2) # Trueprint(p1 != p3) # Trueprint(p1 > p3) # Trueprint(p1 >= p3) # Trueprint(p1 < p3) # False
r = ec.Permissions.READw = ec.Permissions.WRITE
perm = r | wprint(int(perm)) # 3
has_read = perm & rprint(has_read == r) # True
all_perms = ec.Permissions.READ | ec.Permissions.WRITE | ec.Permissions.EXECUTEprint(int(all_perms)) # 7py::arithmetic() vs 无算术运算
Section titled “py::arithmetic() vs 无算术运算”| 模式 | 支持的操作 |
|---|---|
无 py::arithmetic() | ==, !=, <, >, <=, >= |
有 py::arithmetic() | 以上全部 + +, -, &, ` |
何时使用: 如果枚举用于标志位(如权限、选项组合),需要
py::arithmetic()。普通顺序枚举不需要。
6.4 枚举转字符串
Section titled “6.4 枚举转字符串”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
enum class HttpStatus { OK = 200, NOT_FOUND = 404, INTERNAL_ERROR = 500};
PYBIND11_MODULE(enum_to_string, m) { py::enum_<HttpStatus>(m, "HttpStatus", "HTTP 状态码") .value("OK", HttpStatus::OK) .value("NOT_FOUND", HttpStatus::NOT_FOUND) .value("INTERNAL_ERROR", HttpStatus::INTERNAL_ERROR) .export_values();
// 添加自定义方法 py::enum_<HttpStatus>(m, "HttpStatusEx", "HTTP 状态码(扩展)") .value("OK", HttpStatus::OK) .value("NOT_FOUND", HttpStatus::NOT_FOUND) .value("INTERNAL_ERROR", HttpStatus::INTERNAL_ERROR) .def("__repr__", [](HttpStatus status) { switch (status) { case HttpStatus::OK: return "HttpStatus.OK (200)"; case HttpStatus::NOT_FOUND: return "HttpStatus.NOT_FOUND (404)"; case HttpStatus::INTERNAL_ERROR: return "HttpStatus.INTERNAL_ERROR (500)"; } return "Unknown"; }) .def_property_readonly("code", [](HttpStatus status) { return static_cast<int>(status); }, "获取状态码数值") .export_values();}Python 使用
Section titled “Python 使用”import enum_to_string as ets
status = ets.HttpStatus.OKprint(status) # <HttpStatus.OK: 200>print(repr(status)) # <HttpStatus.OK: 200>
status_ex = ets.HttpStatusEx.OKprint(status_ex) # HttpStatus.OK (200)print(status_ex.code) # 200
print(str(status)) # <HttpStatus.OK: 200>
print(status.name) # OKprint(status.value) # 200枚举常用属性
Section titled “枚举常用属性”print(eb.HttpStatus.OK.name) # "OK"
print(eb.HttpStatus.OK.value) # 200
for status in eb.HttpStatus: print(f"{status.name} = {status.value}")6.5 模块级常量
Section titled “6.5 模块级常量”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
// 常量定义constexpr double PI = 3.141592653589793;constexpr double E = 2.718281828459045;constexpr int MAX_CONNECTIONS = 1000;
// 使用 py::object 导出常量PYBIND11_MODULE(module_constants, m) { m.doc() = "模块级常量演示";
// 直接导出数值常量 m.attr("PI") = py::cast(PI); m.attr("E") = py::cast(E); m.attr("MAX_CONNECTIONS") = py::cast(MAX_CONNECTIONS);
// 导出字符串常量 m.attr("VERSION") = py::str("1.0.0"); m.attr("APP_NAME") = py::str("MyApp");
// 导出复杂常量(字典) py::dict config; config["debug"] = py::bool_(true); config["timeout"] = py::int_(30); config["max_retries"] = py::int_(3); m.attr("DEFAULT_CONFIG") = config;
// 导出列表 py::list supported_versions; supported_versions.append(py::str("1.0")); supported_versions.append(py::str("1.1")); supported_versions.append(py::str("2.0")); m.attr("SUPPORTED_VERSIONS") = supported_versions;}Python 使用
Section titled “Python 使用”import module_constants as mc
print(mc.PI) # 3.141592653589793print(mc.E) # 2.718281828459045print(mc.MAX_CONNECTIONS) # 1000
print(mc.VERSION) # 1.0.0print(mc.APP_NAME) # MyApp
print(mc.DEFAULT_CONFIG) # {'debug': True, 'timeout': 30, 'max_retries': 3}print(mc.DEFAULT_CONFIG["debug"]) # True
print(mc.SUPPORTED_VERSIONS) # ['1.0', '1.1', '2.0']attr() 详解
Section titled “attr() 详解”// 模块级别常量m.attr("NAME") = value;
// 类属性py::class_<MyClass>(m, "MyClass") .attr("CLASS_ATTR") = some_value;| 方法 | 用途 |
|---|---|
m.attr("name") = value | 设置模块级属性/常量 |
.attr("name") = value | 设置类属性 |
注意: 直接使用
m.attr("x") = 5导出int常量可能有类型问题。推荐使用py::cast(value)确保类型正确。
6.6 混合使用:枚举 + 常量
Section titled “6.6 混合使用:枚举 + 常量”C++ 代码
Section titled “C++ 代码”#include <pybind11/pybind11.h>
namespace py = pybind11;
// 错误码枚举enum class ErrorCode { SUCCESS = 0, NOT_FOUND = 404, PERMISSION_DENIED = 403, INTERNAL_ERROR = 500};
// 错误信息映射static const char* ERROR_MESSAGES[] = { "Success", "Not found", "Permission denied", "Internal error"};
PYBIND11_MODULE(mixed_constants, m) { // 绑定枚举 py::enum_<ErrorCode>(m, "ErrorCode", py::arithmetic()) .value("SUCCESS", ErrorCode::SUCCESS) .value("NOT_FOUND", ErrorCode::NOT_FOUND) .value("PERMISSION_DENIED", ErrorCode::PERMISSION_DENIED) .value("INTERNAL_ERROR", ErrorCode::INTERNAL_ERROR) .export_values();
// 添加获取错误信息的方法 m.def("get_error_message", [](ErrorCode code) { return py::str(ERROR_MESSAGES[static_cast<int>(code)]); }, py::arg("code"), "获取错误码对应的错误信息");
// 导出成功标志常量 m.attr("ERR_SUCCESS") = py::cast(static_cast<int>(ErrorCode::SUCCESS)); m.attr("ERR_NOT_FOUND") = py::cast(static_cast<int>(ErrorCode::NOT_FOUND));}Python 使用
Section titled “Python 使用”import mixed_constants as mc
code = mc.ErrorCode.NOT_FOUNDprint(code) # <ErrorCode.NOT_FOUND: 404>print(code.value) # 404
print(mc.ERR_SUCCESS) # 0print(mc.ERR_NOT_FOUND) # 404
print(mc.get_error_message(mc.ErrorCode.NOT_FOUND)) # Not found
def handle_error(code): if code == mc.ErrorCode.SUCCESS: return "OK" elif code == mc.ErrorCode.NOT_FOUND: return "Resource not found" else: return "Unknown error"
print(handle_error(mc.ErrorCode.SUCCESS)) # OKprint(handle_error(mc.ErrorCode.NOT_FOUND)) # Resource not found本章介绍了 pybind11 中枚举和常量的导出方法:
| 主题 | 关键点 |
|---|---|
| 枚举绑定 | py::enum_<Enum>(m, "Name") |
| 枚举值导出 | .value("NAME", Enum::Value) + .export_values() |
| 算术运算 | py::arithmetic() 支持位运算标志位 |
| 枚举比较 | 支持 ==, !=, <, > 等比较操作 |
| 枚举字符串 | 使用 .def("__repr__", ...) 自定义表示 |
| 模块常量 | m.attr("NAME") = py::cast(value) |
| 混合使用 | 枚举 + 常量 + 辅助函数组合使用 |
最佳实践: 为枚举提供有意义的 docstring,在 Python 中能方便地通过
help(MyEnum)查看。
下一章我们将学习智能指针与 pybind11 的集成,这是高级绑定的核心技术之一。