Python动态数组测试用例偶发ValueError: PyObject is NULL问题排查
偶发ValueError: PyObject is NULL问题排查与修复
问题场景
用ctypes实现带类型的Python动态数组,运行指定测试用例时偶尔抛出ValueError: PyObject is NULL,错误并非每次运行都会出现。
实现代码
import ctypes class DynamicArray: def __init__(self,): self.n = 0 # 当前已添加元素数量 self.capacity = 1 # 初始容量为1 self.array = self.make_array(self.capacity) def add_element(self,element): # 检查数组是否已满 if self.n == self.capacity: self.resize(2*self.capacity) self.array[self.n] = element self.n +=1 return def resize(self,new_capacity): new_array = self.make_array(new_capacity) for i in range(self.n): new_array[i] = self.array[i] self.array = new_array self.capacity = new_capacity return def make_array(self,capacity): return (capacity * ctypes.py_object)() def get_array(self): return list(self.array)
测试用例
dynamic_r = DynamicArray() dynamic_r.add_element(4) dynamic_r.get_array() dynamic_r.add_element(9) dynamic_r.get_array() dynamic_r.add_element(10) dynamic_r.get_array()
报错信息
ValueError: PyObject is NULL
问题原因
问题出在get_array方法:list(self.array)会遍历整个ctypes数组的所有容量位置,而不仅仅是已添加的self.n个元素。当数组经过resize扩容后,新分配的数组中除了前self.n个被复制的元素外,剩余位置都是未初始化的ctypes.py_object(对应C层面的NULL指针)。Python尝试将这些NULL指针转换为PyObject时,就会抛出ValueError: PyObject is NULL。
错误偶发是因为Python对ctypes数组的遍历行为可能存在优化,某些情况下未被初始化的位置可能没被实际访问到,但这是不确定的,所以报错不是每次都出现。
修复方案
修改get_array方法,只取数组中前self.n个已初始化的元素:
def get_array(self): return list(self.array[:self.n])
这样转换list时只会遍历已添加的有效元素,不会触及那些未初始化的NULL位置,彻底避免报错。
内容的提问来源于stack exchange,提问作者Ahmed Fakhry
相关产品推荐
相关产品推荐

