在__init_subclass__中用ctypes.POINTER创建结构体子类指针报错排查
问题:ctypes创建指针时触发
TypeError: _type_ must have storage info错误 报错代码示例
执行第6行p = ctypes.POINTER(cls)时触发错误:
import ctypes from ctypes.wintypes import * class xStructure(ctypes.Structure): def __init_subclass__(cls): p = ctypes.POINTER(cls) class OFFSET_VAL_II_C4(xStructure): _fields_ = [('ValOrig', DOUBLE), ('MaxChangeVal', DOUBLE), ('ActChangeVal', DOUBLE)]
正常运行代码示例
import ctypes from ctypes.wintypes import * class xStructure(ctypes.Structure): pass class OFFSET_VAL_II_C4(xStructure): _fields_ = [('ValOrig', DOUBLE), ('MaxChangeVal', DOUBLE), ('ActChangeVal', DOUBLE)] p = ctypes.POINTER(OFFSET_VAL_II_C4)
原因分析
核心问题在于__init_subclass__的执行时机:
- 当定义
OFFSET_VAL_II_C4并继承xStructure时,Python会立即调用父类的__init_subclass__方法,此时cls(即OFFSET_VAL_II_C4)已经被创建,但还未设置_fields_属性。 - ctypes要求结构体类必须设置
_fields_后,才会生成对应的存储信息(用于和C类型交互的底层数据结构),此时尝试创建指针ctypes.POINTER(cls),ctypes会认为这个类不是合法的ctype类型,因此抛出TypeError。 - 第二个示例中,
ctypes.POINTER(OFFSET_VAL_II_C4)是在OFFSET_VAL_II_C4的_fields_完全设置完成后才执行的,此时类已经具备了ctype所需的存储信息,所以能正常运行。
解决方案
如果需要在父类的__init_subclass__中创建子类的指针类型,需要确保子类的_fields_已经被处理完毕。可以通过以下方式实现:
方式1:延迟指针创建逻辑
将指针创建逻辑放到子类实例化或其他后续时机,比如定义一个类方法:
import ctypes from ctypes.wintypes import * class xStructure(ctypes.Structure): @classmethod def get_pointer_type(cls): return ctypes.POINTER(cls) class OFFSET_VAL_II_C4(xStructure): _fields_ = [('ValOrig', DOUBLE), ('MaxChangeVal', DOUBLE), ('ActChangeVal', DOUBLE)] # 此时调用已没问题 p = OFFSET_VAL_II_C4.get_pointer_type()
方式2:利用元类确保_fields_初始化完成
通过自定义元类,在类完全创建(包括_fields_处理)后再生成指针类型:
import ctypes from ctypes.wintypes import * class StructureMeta(ctypes.Structure.__class__): def __new__(meta, name, bases, namespace): cls = super().__new__(meta, name, bases, namespace) # 此时_fields_已经被ctypes处理完毕 cls.pointer_type = ctypes.POINTER(cls) return cls class xStructure(ctypes.Structure, metaclass=StructureMeta): pass class OFFSET_VAL_II_C4(xStructure): _fields_ = [('ValOrig', DOUBLE), ('MaxChangeVal', DOUBLE), ('ActChangeVal', DOUBLE)] # 直接使用子类的pointer_type属性 p = OFFSET_VAL_II_C4.pointer_type
内容的提问来源于stack exchange,提问作者TruffMonk
相关产品推荐
相关产品推荐

