如何在PyO3中返回带Payload(负载)的自定义错误?
在PyO3中实现带负载的自定义错误
PyO3的官方示例大多仅返回简单字符串错误,但实际开发中常需要返回包含额外负载(比如错误元素及其位置信息)的自定义错误。以下是Python中的等价实现:
from dataclasses import dataclass @dataclass class NotIntError(Exception): item: object def __str__(self): return f"Expected int but found {type(self.item)}: {self.item}" def validate_int(item): if not isinstance(item, int): raise NotIntError(item)
你的Rust代码思路方向正确,但有几处需要调整,以下是修正后的完整实现:
use pyo3::prelude::*; use pyo3::types::PyInt; // 自定义错误类,继承Python的Exception #[pyclass(extends=PyException)] #[derive(Debug)] pub struct NotIntError { #[pyo3(get)] // 暴露item属性给Python访问 item: Py<PyAny>, } #[pymethods] impl NotIntError { // 初始化方法,规范错误实例的创建方式 #[new] fn new(item: Py<PyAny>) -> Self { NotIntError { item } } // 实现字符串表示,符合Python的__str__行为 fn __str__(&self, py: Python<'_>) -> PyResult<String> { let item_type = self.item.get_type(py).name()?; let item_str = self.item.str(py)?.to_string(); Ok(format!("Expected int but found {}: {}", item_type, item_str)) } } #[pyfunction] pub fn validate_int(item: &Bound<'_, PyAny>) -> PyResult<()> { if !item.is_instance_of::<PyInt>() { // 创建自定义错误实例并抛出 let error = NotIntError::new(item.into()); Err(PyErr::new::<NotIntError, _>(error)) } else { Ok(()) } } // 模块注册,让Python能访问自定义错误和函数 #[pymodule] fn my_module(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::<NotIntError>()?; m.add_function(wrap_pyfunction!(validate_int, m)?)?; Ok(()) }
关键调整说明
- 在
#[pyclass]中添加extends=PyException,让自定义类成为Python Exception的子类,融入Python错误体系 - 用
#[pyo3(get)]标记item字段,允许Python代码直接访问错误实例的item属性 - 实现
#[new]方法,统一错误实例的创建逻辑 - 在
__str__中使用PyAny的str()方法获取符合Python风格的字符串表示,避免Debug格式的输出问题
使用效果
在Python中调用时,行为和你给出的Python实现完全一致:
import my_module try: my_module.validate_int("hello") except my_module.NotIntError as e: print(e) # 输出: Expected int but found str: hello print(e.item) # 输出: hello
内容的提问来源于stack exchange,提问作者drhagen
相关产品推荐
相关产品推荐

