修复 Arc::make_mut 遗留问题

增加 基础数据对比方法
This commit is contained in:
YuWuKunCheng
2026-06-05 16:35:31 +08:00
parent 823a24d364
commit e33b1f0744
17 changed files with 1809 additions and 24 deletions
+808
View File
@@ -0,0 +1,808 @@
/*
* MIT License
*
* Copyright (c) 2026 YuYuKunKun
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use std::num::NonZeroUsize;
use std::sync::Mutex;
use lru::LruCache;
use pyo3::prelude::*;
/// 缓存辅助宏:在调用点创建静态 LruCache,先查后存
macro_rules! with_cache {
($cache:ident, $size:literal, $key_expr:expr, $compute:expr) => {{
use std::sync::LazyLock;
static $cache: LazyLock<Mutex<LruCache<(usize, usize, i64), (bool, String)>>> =
LazyLock::new(|| Mutex::new(LruCache::new(NonZeroUsize::new($size).unwrap())));
let key = $key_expr;
if let Some(cached) = $cache.lock().unwrap().get(&key) {
return Ok(cached.clone());
}
let result: PyResult<(bool, String)> = $compute;
if let Ok(ref r) = result {
$cache.lock().unwrap().put(key, r.clone());
}
result
}};
}
/// 从 Python 值中提取时间戳(兼容 i64 和 datetime 两种类型)
fn (val: &Bound<'_, PyAny>) -> PyResult<i64> {
if let Ok(ts) = val.extract::<i64>() {
return Ok(ts);
}
let ts_f: f64 = val.call_method0("timestamp")?.extract()?;
Ok(ts_f as i64)
}
/// 从对象获取属性,依次尝试多个候选名
fn <'a>(
obj: &'a Bound<'_, PyAny>,
: &[&str],
) -> PyResult<Option<Bound<'a, PyAny>>> {
for name in {
if obj.hasattr(name)? {
return Ok(Some(obj.getattr(name)?));
}
}
Ok(None)
}
/// 比较两个 Python 值是否为 float(容差比较)
fn (
valA: &Bound<'_, PyAny>,
valB: &Bound<'_, PyAny>,
: f64,
) -> Option<PyResult<(bool, String)>> {
if let (Ok(a), Ok(b)) = (valA.extract::<f64>(), valB.extract::<f64>()) {
if (a - b).abs() > {
return Some(Ok((
false,
format!("浮点超限 容差={:.2e} A={:.10},B={:.10}", , a, b),
)));
}
return Some(Ok((true, String::new())));
}
None
}
/// 尝试从对象获取 `标识` 字段,失败返回空字符串
fn (obj: &Bound<'_, PyAny>) -> String {
if let Ok(val) = obj.getattr("标识")
&& let Ok(py_str) = val.str()
{
return py_str.extract::<String>().unwrap_or_default();
}
String::new()
}
/// None 检查辅助:双方为 None 返回 true,单方为 None 返回 false+消息
fn (
valA: &Bound<'_, PyAny>,
valB: &Bound<'_, PyAny>,
: &str,
: &str,
) -> Option<(bool, String)> {
let a_none = valA.is_none();
let b_none = valB.is_none();
if a_none && b_none {
return Some((true, String::new()));
}
if a_none || b_none {
return Some((
false,
format!("{标签}: [{字段}] 空值不一致 A=None={a_none},B=None={b_none}"),
));
}
None
}
// ========== K线相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn K线相等(
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_KLINE,
128,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
// 快速路径
if let (Ok(a), Ok(b)) = (
A.cast::<crate::kline_py::K线Py>(),
B.cast::<crate::kline_py::K线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
// 回退路径
let = "K线校验";
let = [
"标识",
"序号",
"周期",
"时间戳",
"",
"",
"开盘价",
"收盘价",
"成交量",
];
for & in & {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在属性 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在属性 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if let Some(r) = (&valA, &valB, ) {
let (ok, m) = r?;
if !ok {
return Ok((false, format!("{标签}: [{字段}]{}", m)));
}
} else if == "时间戳" {
let a = (&valA).unwrap_or(0);
let b = (&valB).unwrap_or(0);
if a != b {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={a},B={b}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 全部字段一致")))
}
)
}
// ========== 缠论K线相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn K线相等(
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_CHAN_K,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::kline_py::K线Py>(),
B.cast::<crate::kline_py::K线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let = "缠论K线校验";
let = [
"序号",
"时间戳",
"",
"",
"方向",
"分型",
"周期",
"标识",
"分型特征值",
"原始起始序号",
"原始结束序号",
"标的K线",
"买卖点信息",
];
for & in & {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if let Some(r) = (&valA, &valB, ) {
let (ok, m) = r?;
if !ok {
return Ok((false, format!("{标签}: [{字段}]{m}")));
}
} else if == "标的K线" {
if let Some(r) = (&valA, &valB, , ) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = K线相等(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: 标的K线子项异常 >> {msg}")));
}
} else if == "时间戳" {
let a = (&valA).unwrap_or(0);
let b = (&valB).unwrap_or(0);
if a != b {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={a},B={b}")));
}
} else if == "方向" || == "分型" {
let sa = valA.str()?.extract::<String>().unwrap_or_default();
let sb = valB.str()?.extract::<String>().unwrap_or_default();
if sa != sb {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={sa},B={sb}")));
}
} else if == "买卖点信息" {
let py = A.py();
let set_a = py.import("builtins")?.getattr("set")?.call1((&valA,))?;
let set_b = py.import("builtins")?.getattr("set")?.call1((&valB,))?;
let eq: bool = set_a.eq(set_b)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 全部字段嵌套校验一致")))
}
)
}
// ========== 分型相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn (
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_FRACTAL,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::structure_py::Py>(),
B.cast::<crate::structure_py::Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let = "分型校验";
// Python 分型内部用 _结构/_时间戳/_分型特征值 作为 slot 名,Rust 用 结构/时间戳/分型特征值 作为 getter
for & in &["", "", ""] {
let valA = A.getattr()?;
let valB = B.getattr()?;
if let Some(r) = (&valA, &valB, , ) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = K线相等(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]缠论K线子项异常 >> {msg}")));
}
}
for &(, ) in &[
("_结构", "结构"),
("_时间戳", "时间戳"),
("_分型特征值", "分型特征值"),
] {
// 先尝试 Python 侧的下划线名,再尝试 Rust 侧的无下划线名
let valA = (A, &[, ])?;
let valB = (B, &[, ])?;
let (a有, b有) = (valA.is_some(), valB.is_some());
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在属性 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在属性 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = valA.unwrap();
let valB = valB.unwrap();
if let Some(r) = (&valA, &valB, ) {
let (ok, m) = r?;
if !ok {
return Ok((false, format!("{标签}: [{字段}]{m}")));
}
} else if == "_时间戳" {
let a = (&valA).unwrap_or(0);
let b = (&valB).unwrap_or(0);
if a != b {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={a},B={b}")));
}
} else if == "_结构" {
let sa = valA.str()?.extract::<String>().unwrap_or_default();
let sb = valB.str()?.extract::<String>().unwrap_or_default();
if sa != sb {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={sa},B={sb}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 自有字段+三根缠论K线全部校验一致")))
}
)
}
// ========== 缺口相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn (
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_GAP,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::types_py::Py>(),
B.cast::<crate::types_py::Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let = "缺口校验";
for & in &["", ""] {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if let Some(r) = (&valA, &valB, ) {
let (ok, m) = r?;
if !ok {
return Ok((false, format!("{标签}: [{字段}]{m}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 上下沿价格校验完全一致")))
}
)
}
// ========== 线段特征相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn 线(
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_SEG_FEAT,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::structure_py::线Py>(),
B.cast::<crate::structure_py::线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let = "线段特征校验";
for & in &["序号", "标识", "线段方向", "基础序列"] {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if == "基础序列" {
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [基础序列] 列表长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
let (eq, msg) = 线(&itemA, &itemB, )?;
if !eq {
return Ok((
false,
format!("{标签}: 基础序列[{idx}]子虚线异常 >> {msg}"),
));
}
}
} else if == "线段方向" {
let sa = valA.str()?.extract::<String>().unwrap_or_default();
let sb = valB.str()?.extract::<String>().unwrap_or_default();
if sa != sb {
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={sa},B={sb}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 字段与内部虚线序列全部一致")))
}
)
}
// ========== 中枢相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn (
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_HUB,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::algorithm_py::Py>(),
B.cast::<crate::algorithm_py::Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let a标识 = (A);
let b标识 = (B);
let = format!("中枢校验[A标识={a标识},B标识={b标识}]");
for & in &[
"序号",
"标识",
"级别",
"基础序列",
"第三买卖线",
"本级_第三买卖线",
] {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if == "基础序列" {
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [基础序列] 长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
let (eq, msg) = 线(&itemA, &itemB, )?;
if !eq {
return Ok((
false,
format!("{标签}: 基础序列[{idx}]虚线异常 >> {msg}"),
));
}
}
} else if == "第三买卖线" || == "本级_第三买卖线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子虚线异常 >> {msg}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 基础序列+第三买卖线全部校验一致")))
}
)
}
// ========== 虚线相等 ==========
#[pyfunction]
#[pyo3(signature = (A, B, 浮点容差 = 1e-9))]
fn 线(
A: &Bound<'_, PyAny>,
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_DASH,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::structure_py::线Py>(),
B.cast::<crate::structure_py::线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let a标识 = (A);
let b标识 = (B);
let = format!("虚线校验[A标识={a标识},B标识={b标识}]");
let = [
"标识",
"序号",
"级别",
"",
"",
"有效性",
"基础序列",
"特征序列",
"实_中枢序列",
"虚_中枢序列",
"合_中枢序列",
"确认K线",
"模式",
"_特征序列_显示",
"前一缺口",
"前一结束位置",
"短路修正",
];
for & in & {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在属性 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在属性 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
// 文/武:分型
if == "" || == "" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子分型异常 >> {msg}")));
}
}
// 前一缺口
else if == "前一缺口" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一缺口]子缺口异常 >> {msg}")));
}
}
// 前一结束位置
else if == "前一结束位置" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一结束位置]异常 >> {msg}")));
}
}
// 确认K线
else if == "确认K线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = K线相等(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [确认K线]子缠论K线异常 >> {msg}")));
}
}
// 各类列表
else if == "基础序列"
|| == "实_中枢序列"
|| == "虚_中枢序列"
|| == "合_中枢序列"
|| == "特征序列"
{
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [{字段}]列表长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
if let Some(r) =
(&itemA, &itemB, &format!("{字段}[{idx}]"), &)
{
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = if == "基础序列" {
线(&itemA, &itemB, )?
} else if .contains("中枢") {
(&itemA, &itemB, )?
} else {
线(&itemA, &itemB, )?
};
if !eq {
return Ok((
false,
format!("{标签}: [{字段}][{idx}]子项异常 >> {msg}"),
));
}
}
}
// 普通字段
else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}]数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 全字段所有嵌套子结构校验一致")))
}
)
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(K线相等, m)?)?;
m.add_function(wrap_pyfunction!(K线相等, m)?)?;
m.add_function(wrap_pyfunction!(, m)?)?;
m.add_function(wrap_pyfunction!(, m)?)?;
m.add_function(wrap_pyfunction!(线, m)?)?;
m.add_function(wrap_pyfunction!(, m)?)?;
m.add_function(wrap_pyfunction!(线, m)?)?;
Ok(())
}
+3
View File
@@ -103,6 +103,7 @@ fn init_tracing() {
mod algorithm_py;
mod business_py;
mod config_py;
mod equality_py;
mod indicators_py;
mod kline_py;
mod structure_py;
@@ -175,6 +176,8 @@ fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
algorithm_py::register(m)?;
// 阶段 7: 业务
business_py::register(m)?;
// 阶段 8: 相等校验函数
equality_py::register(m)?;
Ok(())
}