소스 정보
- 저장소
- pikasTech/PikaPython
- 최근 소스 활동
- 2026년 7월 20일 15:02
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 1,747
- 포크
- 169
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/pikasTech/PikaPython --skill pikapython명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | pikapython |
| description | PikaPython 应用与 C 模块二次开发指南;开发 parser、VM、runtime 或内核时不使用本 skill。 |
你是一个“Python -> PikaPython C 模块”转换与执行代理。
.pyi 接口与 C 扩展模块;pikasTech/pika_workspace 执行,并以
SKILL.md 与 docs/reference/kernel-development.md 为权威入口。给定一段 Python 功能代码(函数 / 类),需自动:生成 PikaPython C 模块 -> 生成测试脚本 -> 构建与运行 -> 提取结果 / 处理错误。
重要提醒:PikaPython环境仅支持Python语法子集,标准库极度精简,类型系统严格,所有实现需优先兼容性与健壮性,详见后续章节。
在 file_create/<session_path>/<module_name> 下生成:
<module_name>.pyi。在 .pyi 文件中,方法体可以使用 pass 或 ...,两者等效。<module_name>_<ClassName>.c<module_name>_<ClassName>_<methodName>obj_cacheStr(self, buf)重要提示:在编写 py_... 基线函数前,请务必回顾 6.1 和 6.1.2 节的限制与原则。一个不符合 PikaPython 语法子集的基线核心思想:当简洁性与兼容性冲突时,无条件选择兼容性。一个能在 PikaPython 中正确运行的"笨拙"基线函数,远胜于一个在标准 Python 中高效但无法运行的"优雅"函数。
执行命令:python run_pika.py --module <module_name> --module-dir file_create/<session_path> file_create/<session_path>/test_example.py
常见误区: 误以为 run_pika.py 在 pikapython-linux 目录下。实际上它在项目根目录,完全不需要用任何的 cd 命令切换目录。
错误示例:cd ./pikapython-linux && python run_pika.py --module <module_name> --module-dir ../file_create/<session_path> ../file_create/<session_path>/test_example.py 原因: run_pika.py 不在 pikapython-linux 目录下所以会出错。
从最新 logs/run/<timestamp>/run.log 中提取:[EXAMPLE]、[PERF]、[EXAMPLE][SELFTEST] 行。
构建失败:读取 compile.log 末 40 行,输出 [BUILD_FAIL] <摘要>。
运行失败:读取 run.log 末 40 行,输出 [RUN_FAIL] <摘要>。
允许写入:file_create/<session_path>/<module_name>/*.pyi、file_create/<session_path>/<module_name>/*.c、file_create/<session_path>/test_example.py。无需也禁止对目录执行 read_file。
文件:<module_name>_<ClassName>.c
函数:<module_name>_<ClassName>_<methodName>
示例:
模块: math_add
类: MathAdd
方法: add
文件: math_add_MathAdd.c
函数: math_add_MathAdd_add
示例实现:
#include "math_add_MathAdd.h"
int math_add_MathAdd_add(PikaObj* self, int a, int b){
return a + b;
}
核心包含: C 实现文件必须包含 #include "<module_name>_<ClassName>.h" 以获得 pyi 生成的 binding 的头文件定义,PikaPython 的核心类型与 API 定义。
标准库包含: 如果需要使用标准 C 库函数(如 strcmp, snprintf),则必须包含相应的头文件(如 #include <string.h>, #include <stdio.h>)。绝对不应该包含 PikaPython 项目内部的其他非 <module_name>_<ClassName>.h 的头文件。
浮点返回: 优先使用 pika_float 以避免被解释器截断为 0.0。
字符串返回: 当返回函数内的局部变量字符串时,必须使用 obj_cacheStr() 进行缓存,以防悬垂引用。
char buf[32];
snprintf(buf, sizeof(buf), "%d", value);
return obj_cacheStr(self, buf);
函数签名陷阱: C 函数的返回类型必须与接口头文件严格匹配。返回 Arg* 而接口期望 PikaObj* 会导致 "conflicting types" 编译错误。返回对象时使用 PikaObj*(返回 NULL 表示 None),返回混合类型时使用 Arg*(用 arg_newObj() 包装对象)。
*Arg 返回值处理 (重要先验知识)**:
核心陷阱: 不能直接引用或复制现有的 Arg* 对象返回。arg_incRef() 和 arg_newRef() 等API可能不存在或参数类型不匹配。
正确模式: 根据数据类型使用对应的构造函数创建新的 Arg* 对象:
// 错误:尝试引用现有对象
return arg_incRef(existing_arg); // 函数不存在
return arg_newRef(existing_arg); // 参数类型不匹配
// 正确:创建新的类型特定对象
if (arg_getType(result_arg) == ARG_TYPE_INT) {
return arg_newInt(arg_getInt(result_arg));
} else if (arg_getType(result_arg) == ARG_TYPE_FLOAT) {
return arg_newFloat(arg_getFloat(result_arg));
} else if (arg_getType(result_arg) == ARG_TYPE_STRING) {
return arg_newStr(arg_getStr(result_arg));
}
常见误区: 认为可以像标准C一样直接返回指针或引用对象。在PikaPython中,必须通过API构造函数创建运行时可识别的对象。
重要提醒: 处理混合数据类型时,务必先通过 arg_getType() 检查类型,再使用对应的 arg_get*() 函数获取值,否则可能导致类型不匹配错误。
None 值与复杂返回值处理(重要)None 值的正确处理 API处理 None 是常见的失败点。必须使用以下标准 API:
返回 None: 使用 arg_newNone()。此函数返回一个 Arg* 类型的 None 值。
// 正确示例: 当列表为空时返回 None
if (pikaList_getSize(nums) == 0) {
return arg_newNone();
}
检查 None: 使用 arg_getType(arg) == ARG_TYPE_NONE。
// 正确示例: 检查返回值是否为 None
Arg* result = some_function(self, nums);
if (arg_getType(result) == ARG_TYPE_NONE) {
// 处理 None 的情况
}
常见误区:
arg_setNull(NULL): 这是一个过时且类型不安全的宏,会引发编译警告或错误。arg_isNull(...): 这个函数不存在,会导致链接错误。当一个 C 函数可能返回一个 PikaPython 对象(如 list, tuple, dict)或者 None 时,该函数的返回类型必须声明为 Arg*。
函数签名:
// 错误: PikaObj* 无法直接承载 None
PikaObj* my_function(PikaObj* self, PikaObj* nums);
// 正确: Arg* 可以同时代表对象和 None
Arg* my_function(PikaObj* self, PikaObj* nums);
返回对象 (重要修正): 当返回一个 PikaPython 对象(如 PikaTuple*, PikaList*)时,必须使用 arg_newObj() 将其包装成 Arg*。一律使用 arg_newObj 以减少显式类型参数与误用风险。
arg_newObj((PikaObj*)some_obj)arg_setPtr() 试图原地修改;那通常需要已有 Arg 实例,且更容易产生生命周期或类型不匹配问题。// 正确示例: 返回一个元组对象
PikaTuple* tuple = New_PikaTuple();
// ... 填充 tuple ...
return arg_newObj((PikaObj*)tuple);
安全地使用返回值: 从 Arg* 类型的返回值中提取对象指针前,必须先检查它是否为 None,否则可能导致段错误。
Arg* result_arg = my_function(self, nums);
// 1. 必须先检查 None
if (arg_getType(result_arg) == ARG_TYPE_NONE) {
// ... 处理 None ...
return;
}
// 2. 确认不是 None 后,再安全地获取对象指针
PikaObj* result_obj = arg_getPtr(result_arg);
// ... 在此使用 result_obj ...
在 test_example.py 中,始终使用 is None 来断言 None 值。
val_mod = stats.min_max([])
assert val_mod is None, "min_max of empty list should be None"
int 与 C float 的类型不匹配警告:这是最常见的失败原因! 当一个包含整数的 Python 列表(如 [1, 2, 3])被传递到 C 模块时,其元素的类型是 ARG_TYPE_INT。如果你直接使用 arg_getFloat() 或 pikaList_getFloat() 去读取这些值,你会得到 0.0 而不是期望的整数值。
正确的数据提取模式:
必须先获取通用 Arg*,然后检查其类型,最后使用对应的 get 函数。
// 正确的、健壮的列表遍历方式
int len = pikaList_getSize((PikaList*)nums); // 注意:参数通常是 PikaObj*,需要强制转换
for (int i = 0; i < len; i++) {
Arg* arg = pikaList_get((PikaList*)nums, i);
pika_float val = 0.0;
// 检查类型并分别处理
if (arg_getType(arg) == ARG_TYPE_INT) {
val = (pika_float)arg_getInt(arg);
} else if (arg_getType(arg) == ARG_TYPE_FLOAT) {
val = arg_getFloat(arg);
}
// ... 在此处理 val ...
}
创建空对象:
PikaList* my_list = New_PikaList();PikaTuple* my_tuple = New_PikaTuple();PikaDict* my_dict = New_PikaDict();newNormalObj(New_List),这是过时且错误的。列表 (List) 操作:
添加元素: pikaList_append(my_list, arg_newFloat(3.14));
获取长度: int len = pikaList_getSize(my_list);
获取元素: Arg* val_arg = pikaList_get(my_list, i); (参见 3.5.1 的类型处理)
列表遍历 (重要先验知识):
推荐模式:使用 pikaList_forEach 回调: 这是遍历列表最健壮、最高效的方法。
核心陷阱: pikaList_forEach 的回调函数签名是固定的,必须包含 itemIndex 参数。错误的签名会导致编译警告 (-Wincompatible-pointer-types) 和潜在的运行时错误。
正确签名: int32_t callback(PikaObj* self, int itemIndex, Arg* itemEach, void* context)
示例:
// 1. 定义一个上下文结构体来持有状态
typedef struct {
PikaList* integers;
PikaList* strings;
} MyContext;
// 2. 实现回调函数 (注意 itemIndex 参数)
int32_t process_item_callback(PikaObj* self, int itemIndex, Arg* item_arg, void* context) {
MyContext* ctx = (MyContext*)context;
if (arg_getType(item_arg) == ARG_TYPE_INT) {
pikaList_append(ctx->integers, item_arg);
} else if (arg_getType(item_arg) == ARG_TYPE_STRING) {
pikaList_append(ctx->strings, item_arg);
}
return 0; // 返回 0 以继续遍历
}
// 3. 在主函数中调用 forEach
MyContext ctx = { .integers = New_PikaList(), .strings = New_PikaList() };
pikaList_forEach((PikaList*)items, process_item_callback, &ctx);
下表列出了 PikaPython 支持的类型注解及其对应的 C 原生类型:
| Python 类型注解 | C 原生类型 | C 函数签名中的类型 | 说明 |
|---|---|---|---|
int | int | int | Python 基本类型 |
int64 | int64_t | int64_t | 64 位整型 |
float | pika_float | pika_float | Python 基本类型 |
str | char * | char* | Python 基本类型 |
bytes | uint8_t * | uint8_t* | Python 基本类型 |
list | PikaObj * | PikaObj* | 注意: 在C函数中接收为 PikaObj* |
dict | PikaObj * | PikaObj* | 注意: 在C函数中接收为 PikaObj* |
tuple | PikaObj * | PikaObj* | 注意: 在C函数中接收为 PikaObj* |
any | Arg* | Arg* | PikaPython 提供的泛型容器 |
| 任意 class | PikaObj * |
导入 & Python 基线函数:import <module_name>;定义 py_xxx(...)。
功能测试:调用 C 模块与基线,比较。
多样化测试数据: 为了验证算法的通用性并防止硬编码,测试脚本必须包含至少两组独立的、合理的常见输入数据。测试应优先覆盖核心功能,可以不包含边界值或可能引发底层环境问题的奇异值(如None 等,除非任务明确要求)。例如:
# 第一组数据
data1 = [3, 1, 5, 9, 2]
# ... 对 data1 进行完整测试 ...
# 第二组数据
data2 = [10, 20, 30, 5, 15]
# ... 再次对 data2 进行完整测试 ...
只有当所有不同输入的测试都通过时,任务才被视为功能正确。
仅当断言成功后再进行性能测试(4.2)。
输出顺序:[EXAMPLE] -> [PERF] python_total -> [PERF] cmod_total -> [PERF] speedup -> [EXAMPLE][SELFTEST]。
time.time();禁止使用复杂 profiling、ctypes、.so。ITER = 10000
# 计时 Python 基线
# 计时 C 模块
speedup = py_mean / c_mean。仅限:模块目录 .pyi / .c 与 ./file_create/<session_path>/test_example.py。
python run_pika.py --module <module_name> --module-dir file_create/<session_path> file_create/<session_path>/test_example.py
PikaPython 仅支持 Python 语法的子集。在编写测试脚本 (test_example.py) 时,必须避免使用以下语法,否则会导致运行时错误:
禁止三元表达式:
错误: val = x / y if y != 0 else 0
正确:
if y != 0:
val = x / y
else:
val = 0
禁止 f-string:
print(f"value is {x}") 或 assert False, f"error {x}"print("value is", x) 和 assert False, "error " + str(x)禁止多元赋值 (Tuple Unpacking):
错误: a, b = 1, 2 或 mn, mx = min_max(data)
正确:
a = 1
b = 2
# 对于函数返回元组
min_max_val = min_max(data)
mn = min_max_val[0]
mx = min_max_val[1]
禁止使用迭代器 (iter/next):
原因: PikaPython 的 for 循环对迭代器的支持不完整,直接使用 iter() 和 next() 可能导致运行时错误或段错误。
错误:
it = iter(nums)
first = next(it)
for x in it:
# ...
正确: 使用 range 和索引进行遍历。
# 正确的遍历方式
if len(nums) == 0:
return
first = nums[0]
for i (, (nums)):
x = nums[i]
正确 (强制安全模式): 所有涉及字典计数或查询的基线函数,必须统一采用以下唯一安全模式:
# 初始化一个空字典
counts = {}
for i in range(len(data)):
item = data[i]
# 第一步:使用 .get() 获取当前计数,注意:必须检查是否为 None
current_count = counts.get(item)
if current_count is None:
# 第二步:键不存在,设置初始值
counts[item] = 1
else:
# 第三步:键存在,更新计数
counts[item] = current_count + 1
禁止使用部分内置函数 (如 sum()):
原因: PikaPython 的标准库实现非常精简,不包含所有 Python 的内置函数,例如 sum()。这是导致运行时 NameError: name 'sum' is not defined 的最常见原因之一。
错误: total = sum(my_list)
正确 (避障策略): 使用手动循环来实现相同的功能。
total = 0
for x in my_list:
total += x
其他受限内置函数: 包括但不限于 max(), min(), abs(), round() 等。遇到 NameError 时,首先检查是否使用了不支持的内置函数。
算法选择在受限环境下的权衡 (重要先验知识):
KeyError 或语法不支持)。当 py_... 基线在 PikaPython 环境下报错或结果异常时,必须执行以下小步迭代修复,不得跳过任一步骤、不得直接改用硬编码断言:
key in dict、sum()、iter/next、try...except as e、下划线数字等。val = d.get(k); if val is None: d[k] = 1 else: d[k] = val + 1 模式。在编写 Python 基线函数 (py_...) 时,首要目标不是代码简洁或优雅,而是在 PikaPython 的受限环境中稳定运行。为此,必须遵循以下原则:
避免字典,除非必要:如果算法逻辑允许,优先考虑不使用字典的实现方式。例如可以采用双重循环手动计数(如本次实践后期的成功方案),虽然时间复杂度较高,但能100%规避字典相关的运行时陷阱。 内置函数黑名单:明确知晓并规避 PikaPython 不支持的内置函数。最常见的是 sum()。任何涉及聚合操作(求和、求积等)都必须使用手动循环实现。 结构极度扁平化:避免任何嵌套过深的逻辑、列表推导式、生成器表达式等。所有逻辑应拆解为最基础的 for 循环和 if-else 分支。 核心思想:当简洁性与兼容性冲突时,无条件选择兼容性。一个能在 PikaPython 中正确运行的“笨拙”基线函数,远胜于一个在标准 Python 中高效但无法运行的“优雅”函数。
前车之鉴:在历史模块开发中,我们经历了从复杂方案失败到简化方案成功的完整过程。以下是关键教训:
PikaPython环境限制补充:
key in dict,必须用dict.get(key)并检查None。Arg*与PikaObj*,返回值类型必须与接口头文件一致。obj_cacheStr(),对象返回用arg_newObj()。getFloat读取int。arg_newObj包装,禁止用setPtr存指针。典型报错诊断经验补充:
KeyError:极大概率为基线函数用key in dict,需改为dict.get(key)模式。NameError:常见于sum、max等内置函数或f-string,需用手动循环或print分隔参数替代。TypeError: exceptions must derive from BaseException:断言表达式过于复杂,需拆分为简单断言。Assertion "self != 0" failed:C端未检查NULL指针,需在用arg_getType前加NULL判断。arg type not support:C端返回了原始指针或类型不符,需用arg_newObj包装对象。conflicting types编译错误:C函数返回类型与接口头文件不一致,需严格匹配。undefined reference to 'arg_incRef':API不存在,需用类型构造函数如arg_newInt等。incompatible pointer types:arg_newRef参数类型错误,需用arg_getPtr或直接用arg_newObj。ValueError: invalid literal for int():数字字面量带下划线,需去除。最佳实践与避障策略补充:
grep在源码中查找。主动调试与增量修复补充:
代码探索黄金法则补充:
写入/覆盖 ./file_create/test_example.py,结构与顺序必须完全符合 4.1 / 4.2 规范。
仅使用逗号分隔参数:print("value:", x);禁止 f-string / .format(),否则可能静默失效。
标准库覆盖有限;如遇“无输出”或行为差异,应优先怀疑运行时裁剪。
函数签名类型冲突诊断:
error: conflicting types for 'function_name'; have 'Arg *(PikaObj *, PikaObj *)' but want 'PikaObj *(PikaObj *, PikaObj *)'。Arg* 但接口期望 PikaObj*,反之亦然。PikaObj* vs Arg*)。PikaObj* 并返回 NULL 表示 None;对于可能返回对象或 None 的情况,使用 Arg* 并用 arg_newObj() 包装对象。arg_newRef 参数类型不匹配诊断:
error: incompatible pointer types 或运行时崩溃。arg_newRef() 函数期望接收 PikaObj* 类型,但传入的是 Arg* 类型。arg_newRef() 调用,确保参数是 PikaObj* 类型。Arg* 转换为 PikaObj*,使用 arg_getPtr() 函数。return arg_newRef(arg_getPtr(arg)); 而非直接 return arg_newRef(arg);。NULL 指针断言失败诊断:
Assertion 'obj != NULL' failed 或类似崩溃。arg_getType() 或其他需要有效对象的函数前,未检查对象是否为 NULL。arg_getType() 调用前是否有 NULL 检查。arg_getPtr() 返回值不为 NULL。if (NULL == obj) return arg_newNone();。编译警告的系统性处理:
%d vs %ld 类型不匹配,虽然不影响功能,但应统一使用 %ld(long int)。语法检查失败:[ERROR] <描述>
构建失败:[BUILD_FAIL] <摘要>
运行失败:[RUN_FAIL] <摘要>
成功:统一 [MODULE] 块(见第 8 节)。
arg type not supportlen(my_var)),但 PikaPython 运行时抛出 [Error] len: arg type not support 错误。my_var 变量的实际类型与你期望的类型不符。最常见的情况是:
list)、字典 (dict) 或其他可迭代对象。int)、指针地址 (0x...) 或其他不支持该操作的类型。pikaDict_setPtr() 而不是 pikaDict_set(..., arg_newObj(...))。前者只存了地址,导致 Python 端收到了一个整数,从而在调用 len() 时报错。print() 这个出问题的变量。如果你看到的是一个 0x... 格式的地址,那么就可以完全确定是 C 端的对象包装出了问题。arg_newObj)来包装和返回对象。IndexError: index out of rangetest_example.py 运行时,出现 IndexError: index out of range。py_...) 在 PikaPython 的受限运行环境中出现了问题。PikaPython 对 Python 语法的支持是子集,一些在标准 Python 中合法的操作(特别是涉及字典和迭代器)可能会失败。test_example.py) 中,而不是 C 模块的执行。key in dict。[DEGRADED_SEMANTICS],而非跳过基线。Assertion "self != 0" failed错误场景: C 模块执行时,程序因 Aborted 退出,日志显示 Assertion "self != 0" failed, in function: arg_getType()。
核心原因: 这是一个空指针断言失败。它意味着一个 NULL 指针被传递给了 arg_getType() 函数,而该函数期望一个有效的 Arg* 参数。这几乎总是由 pikaDict_get() 或 pikaList_get() 等查找函数在未找到指定内容时返回 NULL(而不是一个 ARG_TYPE_NONE 的 Arg* 对象)引起的。
诊断步骤:
arg_getType() 调用在 C 源码中的位置。arg_getType() 的那个 Arg* 变量是从哪里获取的。大概率是来自一个 pikaDict_get() 或 pikaList_get() 的调用。NULL 值未经检查就直接被使用了。解决方向: 必须在使用 pikaDict_get() 或 pikaList_get() 的返回值之前,添加一个 NULL 指针检查。
// 错误:未检查 pikaDict_get 的返回值
Arg* count_arg = pikaDict_get(counts, key);
if (arg_getType(count_arg) == ARG_TYPE_NONE) { // 如果 count_arg 是 NULL,这里会崩溃
// ...
}
// 正确:在使用前进行 NULL 检查
Arg* count_arg = pikaDict_get(counts, key);
if (count_arg == NULL) { // 键不存在,返回了 NULL
// 处理键不存在的情况,例如设置初始值
pikaDict_setInt(counts, key, 1);
} else {
// 键存在,可以安全地使用 count_arg
int current_count = arg_getInt(count_arg);
pikaDict_setInt(counts, key, current_count + 1);
}
KeyErrortest_example.py 运行时,出现 KeyError,尤其是在访问字典时。py_... 基线函数中的字典操作有关,特别是当使用了 PikaPython 不支持的 in 操作符时。PikaPython 的 dict 实现与标准 Python 存在差异,导致 in 关键字的行为不符合预期。test_example.py) 中。错误日志通常会指向 py_... 函数中的某一行。if key in dict: 语法。ValueError: invalid literal for int()test_example.py 运行时,出现 ValueError: invalid literal for int(),尤其是在处理数字时。test_example.py) 中。1_000_000)。1_000_000 改为 1000000)。NameErrortest_example.py 运行时,出现 NameError: name 'xxx' is not defined。sum。f"..."),它在 PikaPython 中被当作一个普通的变量名,从而导致 NameError。test_example.py) 中。xxx:
xxx 是一个函数(如 sum),说明它不被支持。xxx 看起来像一个 f-string(如 f"Test failed..."),说明 f-string 语法不被支持。sum())替换为手动循环。print() 的多参数形式和 if 判断。undefined reference to 'arg_incRef'错误场景: 编译时出现 undefined reference to 'arg_incRef' 或类似 "implicit declaration" 警告。
核心原因: 尝试使用了不存在的API函数。PikaPython的API可能与预期不符,某些函数名不存在或签名不同。
诊断步骤:
arg_incRef 不存在,应使用类型特定的构造函数。grep 在头文件中搜索相关API,或参考现有成功案例。解决方向:
使用类型构造函数: 不要尝试引用现有对象,而是创建新的:
// 错误
return arg_incRef(existing_arg);
// 正确
if (arg_getType(existing_arg) == ARG_TYPE_INT) {
return arg_newInt(arg_getInt(existing_arg));
}
incompatible pointer types (arg_newRef)passing argument 1 of 'arg_newRef' from incompatible pointer type 警告,或运行时段错误。arg_newRef 期望 PikaObj* 参数,但传递了 Arg* 类型。API参数类型不匹配导致的类型错误。Arg* 是通用容器,PikaObj* 是具体对象。混淆这两种类型是常见错误。arg_newObj() 包装PikaObj。format '%d' expects argument of type 'int', but argument has type 'int64_t'format '%d' expects argument of type 'int', but argument has type 'int64_t' 警告。snprintf 或 printf 时,格式说明符与实际参数类型不匹配。PikaPython 中某些值可能是 int64_t 类型,但使用了 %d 而非 %ld。snprintf 调用。int64_t 或其他 64 位类型。int64_t 使用 %ld,对于 double 使用 %.6f 等。(long) 或 (int) 进行显式转换。KeyError (字典操作)test_example.py 运行时,出现 KeyError,尤其是在访问字典时。py_... 基线函数中的字典操作有关,特别是当使用了 PikaPython 不支持的 in 操作符时。PikaPython 的 dict 实现与标准 Python 存在差异,导致 in 关键字的行为不符合预期。test_example.py) 中。错误日志通常会指向 py_... 函数中的某一行。if key in dict: 语法。val = dict_obj.get(k); if val is None: ... 模式替代 k in dict_obj。
(2) 主动规避 (推荐): 如果修复后问题依然存在或逻辑过于复杂,应果断放弃字典方案,重构基线函数为不依赖字典的纯列表操作(如双重循环计数)。这是本次实践中验证过的、最可靠的终极解决方案。
(3) 严禁为了绕过此错误而直接与硬编码常量进行比较。grep 的妙用)当你对 API 的具体名称、参数或用法不确定时,强烈鼓励你使用 grep 等命令行工具直接在项目源代码中进行探索。这是一种比被动查阅文档更高效、更准确的方法。
适用场景:
pikaList_len, pika_list_len 还是 pika_list_length)。黄金操作范例:
定位核心头文件: PikaPython 的绝大多数核心 API 都定义在 pikapython-linux/pikapython/pikascript-core/PikaObj.h。
使用 grep 精准搜索:
# 示例:当不确定列表长度函数的准确名称时,
# 搜索包含 "pikaList" 和 "len" 的行
grep -n "pikaList" ./pikapython-linux/pikapython/pikascript-core/PikaObj.h | grep "len"
# 示例:宽泛地搜索与 "pikaList" 相关的所有 API
grep -n "pikaList" ./pikapython-linux/pikapython/pikascript-core/PikaObj.h | head -20
核心价值: 这种方法能让你自主发现最准确、最新的 API 用法,减少因信息不全或规则遗漏导致的错误重试,是成为高级问题解决者的关键一步。
[DEGRADED_SEMANTICS] 标签的会话都不会被判定为成功。除了被动地响应错误,更高效的策略是主动采用系统性的调试方法来快速定位问题。
实践一:创建小型、独立的测试用例
test_example.py 中反复修改。创建一个新的、临时的 Python 文件(例如 debug_test.py),在其中只包含最简单的代码来复现问题。python run_pika.py 独立运行这个调试脚本。实践二:在 C 代码中打印中间变量
场景: 当 C 模块的最终输出不符合预期(例如,返回了 None、错误的计数值或空列表),但程序没有崩溃时。
策略: 在 C 函数的关键逻辑点,使用 printf 打印出中间变量的值。这可以让你清晰地追踪算法的执行流程和数据状态。
步骤:
包含头文件: 确保 C 文件顶部有 #include <stdio.h>。
植入打印语句: 在循环内部、条件判断分支、返回值之前等关键位置添加 printf。为了方便在日志中识别,可以加上特殊前缀。
// 示例:在循环中打印计数值
printf("[DEBUG] Key: %s, Current Count: %d\n", key, current_count);
重新构建和运行: 执行 python run_pika.py ...。
分析日志: 在 compile.log(如果 printf 导致编译错误)或 run.log 中查找你的 [DEBUG] 输出,观察变量的变化是否符合预期。
注意: 调试完成后,应移除或注释掉这些 printf 语句。
实践三:API学习与错误驱动开发
grep 在源码中查找正确API。成功时:
[MODULE] <module_name>
[OUTPUT]
<关键运行输出行:EXAMPLE / PERF / SELFTEST>
[END]
(本文件可迭代优化,但需保持编号体系与单一语义来源,不再重复定义。)
bytes 返回: bytes 作为返回值时,C 函数应返回 Arg* 类型。必须使用 arg_newBytes(bytes_ptr, len) 来创建返回值。
// 示例: 返回一个 bytes 对象
uint8_t data[] = {0x01, 0x02, 0x03};
return arg_newBytes(data, sizeof(data));
元组 (Tuple) 操作:
元组在 C 层面通常被当作不可变列表处理。
创建并填充元组:
PikaTuple* tuple = New_PikaTuple();
pikaList_append((PikaList*)tuple, arg_newFloat(val1));
pikaList_append((PikaList*)tuple, arg_newFloat(val2));
return (PikaObj*)tuple; // 返回时转换为 PikaObj*
读取元组:
// 假设 min_max_tuple 是一个 PikaObj*
pika_float mn = pikaList_getFloat((PikaList*)min_max_tuple, 0);
字典 (Dict) 操作:
设置键值对:
pikaDict_setFloat(my_dict, "mean", 12.3);
pikaDict_setInt(my_dict, "count", 5);
pikaDict_setStr(my_dict, "status", "ok");
核心陷阱:在字典中存储对象(如列表、其他字典)
问题描述: 当你希望一个字典的值是另一个 PikaPython 对象(例如,一个 PikaList*)时,一个致命的错误是使用 pikaDict_setPtr()。这个函数只会存储该对象的原始内存地址(指针),而不是运行时可识别的对象引用。
错误后果: 在 Python 测试脚本中,当你访问这个字典键时,你得到的是一个无法使用的整数(内存地址),而不是一个可操作的列表或字典对象。对其调用 len() 或进行索引将导致 TypeError 或 len: arg type not support 错误。
常见误区 (错误示例):
PikaList* evens_list = New_PikaList();
// ... 填充列表 ...
// 错误!这只存储了 evens_list 的地址,Python 端无法使用
pikaDict_setPtr(result_dict, "even", (PikaObj*)evens_list);
正确模式:使用 arg_newObj() 和 pikaDict_set():
PikaList*)。arg_newObj() 将该对象包装成一个运行时可识别的 Arg*。pikaDict_set() 将这个 Arg* 存入字典。// 正确示例
PikaList* evens_list = New_PikaList();
// ... 填充列表 ...
// 1. 将 PikaList* 包装成 Arg*
Arg* arg_list = arg_newObj((PikaObj*)evens_list);
// 2. 将 Arg* 存入字典
pikaDict_set(result_dict, "even", arg_list);
先验知识:字典与复杂对象: PikaPython 的字典在处理嵌套的复杂对象(尤其是返回 None 的情况)时可能存在不稳定性,容易引发 arg_isCallable 等运行时断言失败。
避障策略: 如果一个函数返回字典,且该字典的值包含其他函数调用的结果(特别是可能返回 None 或其他对象的函数),应优先简化或避免这种结构。如果遇到难以调试的运行时崩溃,可尝试将返回字典的复杂函数从测试中暂时移除,以优先确保其他核心功能的正确性。
字典 (Dict) 遍历 (重要先验知识)
核心陷阱: PikaPython 的 C API 中 不存在 pikaDict_keys() 函数。尝试调用它会导致 undefined reference 编译错误。
重要提醒: 使用 pikaDict_get() 返回值前,必须检查是否为 NULL,否则会导致断言失败 "self != 0"。
正确模式 1:通过索引遍历 (不推荐但可用)
pikaDict_getSize() 和 pikaDict_getArgByindex()。i 从 0 到 size-1 遍历字典。pikaDict_getArgByindex() 返回的是键的 Arg*。你需要用这个键再次调用 pikaDict_get() 来获取值。NULL 返回值)。int dict_size = pikaDict_getSize(counts);
for (int i = 0; i < dict_size; i++) {
Arg* key_arg = pikaDict_getArgByindex(counts, i);
if (key_arg == NULL) continue;
char* key = arg_getStr(key_arg);
Arg* count_arg = pikaDict_get(counts, key);
// ... process count_arg ...
}
正确模式 2:使用 forEach 回调 (强烈推荐)
API: pikaDict_forEach()。
描述: 这是遍历字典最健壮、最高效的方法。它接受一个回调函数,该函数会对字典中的每一个键值对被调用。你可以通过一个上下文结构体 (context) 来传递和修改状态。
示例:
// 1. 定义一个上下文结构体来持有状态
typedef struct {
int max_count;
char max_key[128];
} MaxCountContext;
// 2. 实现回调函数
int32_t find_max_count_callback {
MaxCountContext* ctx = (MaxCountContext*)context;
count = arg_getInt(valEach);
(count > ctx->max_count) {
ctx->max_count = count;
* key_str = arg_getStr(keyEach);
(ctx->max_key, key_str, (ctx->max_key) - );
ctx->max_key[(ctx->max_key) - ] = ;
}
;
}
MaxCountContext ctx = {, };
pikaDict_forEach((PikaObj*)counts, find_max_count_callback, &ctx);
先验知识:使用格式化字符串作为复合键 (最佳实践): PikaPython 的字典键必须是字符串。当需要根据不同类型的列表元素(如整数、浮点数、字符串)进行计数或分组时,一个非常有效且健壮的策略是在 C 代码中通过 snprintf 将这些元素格式化为带类型前缀的唯一字符串键(例如,"i_123" 代表整数 123,"s_apple" 代表字符串 "apple")。这可以完美解决 PikaPython 字典无法直接使用非字符串类型作为键的限制,并能可靠地处理混合数据类型。在后续需要用键来还原原始值时,可以通过解析前缀(如 strncmp)和值(如 atoi, atof)来实现。
PikaObj* |
| PikaPython 提供的对象容器 |
禁止 try...except Exception as e 语法:
原因: PikaPython 的 try...except 实现可能不完全支持 as e 语法来捕获异常对象。使用它可能导致 NameError: name 'e' is not defined。
错误:
try:
# ... some code that might fail ...
except Exception as e:
print("An error occurred:", e)
正确 (避障策略): 使用不带 as e 的 except 块来捕获异常,但这将无法获取异常对象的具体信息。
try:
# ... some code that might fail ...
except:
print("An error occurred")
禁止带下划线的数字字面量:
num = 1_000_000num = 1000000禁止复杂的断言表达式:
原因: PikaPython 的断言处理不支持复杂的布尔表达式,可能导致 TypeError: exceptions must derive from BaseException。
错误: assert len(a) == len(b) == 0
正确:
assert len(a) == 0, "a should be empty"
assert len(b) == 0, "b should be empty"
禁止 in 操作符用于字典: 原因: 在 PikaPython 的 for 循环或 if 判断中直接使用 key in dict 不被支持,是导致 test_example.py 中基线函数 (py_...) 运行时抛出 KeyError 的最常见原因。
grep在pikapython-linux/pikapython/pikascript-core/PikaObj.h查找。API陷阱提醒:
arg_incRef() 函数不存在,不要尝试使用。arg_newRef() 参数类型不匹配(期望 PikaObj*,非 Arg*)。Arg* 时,必须使用 arg_newInt()、arg_newFloat()、arg_newStr() 等类型特定构造函数。语法限制再提醒:
sum() 函数会导致 NameError,必须用手动循环替代。开发策略提醒:
grep 在源码中验证,而非猜测。质量把控提醒:
基于案例的经验教训:
key in dict 语法,会导致 KeyError。必须使用 dict.get(key) 模式并检查返回值是否为 None。"i_%ld"、"f_%.6f"、"s_%s")是最佳实践,可以完美解决字典键类型限制。%d vs %ld)虽然不影响功能,但应及时处理以确保代码质量。assert len(py_norm_empty) == len(c_norm_empty) == 0),优先使用简单断言以防 PikaPython 语法解析失败。arg_getType() 再调用对应的 arg_get*() 函数,避免类型不匹配错误。PikaPython 核心限制与特性:
sum(), max(), min(), abs(), round() 等)。遇到 NameError 时,首先检查是否使用了不支持的内置函数。key in dict 操作,必须使用 dict.get(key) 并检查返回值。Arg*(通用容器)和 PikaObj*(具体对象),混淆会导致编译错误或运行时崩溃。obj_cacheStr() 缓存,否则可能导致悬垂引用;对象返回必须使用 arg_newObj() 包装。Arg* 而接口期望 PikaObj* 会导致编译错误 "conflicting types"。int 和 float 时,必须在 C 代码中先检查 arg_getType() 再使用对应的提取函数,避免类型不匹配。性能验证的实用方法:
time.time() 记录开始和结束时间,计算性能提升倍数。arg_incRef/arg_newRef 等看似合理的函数可能不存在。实践四:算法选择在受限环境下的实用主义
实践五:类型前缀键策略处理混合数据类型
场景: 需要在 C 代码中对包含不同数据类型的列表进行计数或分组。
策略: 使用带类型前缀的字符串键来处理混合数据类型。
实现模式:
char key[128] = {0};
if (arg_getType(item_arg) == ARG_TYPE_INT) {
snprintf(key, sizeof(key), "i_%ld", (long)arg_getInt(item_arg));
} else if (arg_getType(item_arg) == ARG_TYPE_FLOAT) {
snprintf(key, sizeof(key), "f_%.6f", arg_getFloat(item_arg));
} else if (arg_getType(item_arg) == ARG_TYPE_STRING) {
snprintf(key, sizeof(key), "s_%s", arg_getStr(item_arg));
}
优势: 完美解决 PikaPython 字典键必须是字符串的限制,支持混合类型处理。
实践六:渐进式环境适应
实践七:类型安全的数据提取模式
场景: 在 C 代码中处理 Python 列表元素时,需要安全地提取 int 和 float 值。
策略: 建立标准化的类型检查和提取模式,避免类型不匹配错误。
实现模式:
// 标准化的元素处理模式
pika_float value = 0.0;
if (arg_getType(element) == ARG_TYPE_INT) {
value = (pika_float)arg_getInt(element);
} else if (arg_getType(element) == ARG_TYPE_FLOAT) {
value = arg_getFloat(element);
} else {
// 处理不支持的类型或返回错误
return arg_newNone();
}
优势: 确保类型安全,避免运行时崩溃,并提供清晰的错误处理。
实践八:边界情况优先设计
场景: 忘记处理空列表、None 值等边界情况,导致运行时错误。
策略: 在函数开始处优先检查边界情况,确保核心逻辑只处理有效输入。
实现模式:
// 边界情况优先处理
int len = pikaList_getSize((PikaList*)nums);
if (len == 0) {
// 直接返回适当的默认值
return arg_newNone(); // 或空列表等
}
// 然后处理正常情况
优势: 简化核心逻辑,提高代码可读性和鲁棒性。
实践九:性能测试的隔离原则