Python:结构体和数组与 ctypes 中的类似功能

2024-05-17

Python 提供了以下三个处理 C 类型以及如何处理它们的模块:

  • struct https://docs.python.org/3/library/struct.html对于 C 结构体
  • array https://docs.python.org/3/library/array.html对于数组,例如 C 中的数组
  • ctypes https://docs.python.org/3/library/ctypes.html对于 C 函数,这必然需要处理 C 的类型系统

While ctypes看起来更通用和灵活(它的主要任务是“Python 的外部函数库”)struct and array,当任务是读取二进制数据结构时,这三个模块之间的功能似乎存在显着重叠。例如,如果我想读取 C 结构体

struct MyStruct {
    int a;
    float b;
    char c[12];
};

我可以用struct如下:

a, b, c = struct.unpack('if12s', b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
print(a, b, c)
# 17 1.7378244361449504e+34 b'hello world\x00'

另一方面,using ctypes同样有效 https://stackoverflow.com/a/1972348(虽然有点冗长):

 class MyStruct(ctypes.Structure):
     _fields_ = [
         ('a', ctypes.c_int),
         ('b', ctypes.c_float),
         ('c', ctypes.c_char * 12)
     ]
 s = MyStruct.from_buffer_copy(b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
 print(s.a, s.b, s.c)
 # 17 1.7378244361449504e+34 b'hello world'

(旁白:我确实想知道尾随在哪里'\0'不过,进入了这个版本……)

在我看来,这违反了“Python之禅”中的原则:

  1. 应该有一种(最好只有一种)明显的方法来做到这一点。

那么,用于二进制数据处理的几个类似模块的这种情况是如何出现的呢?有历史或现实原因吗? (例如,我可以想象省略struct完全模块并简单地添加一个更方便的 API 来读取/写入 C 结构ctypes.)


免责声明:这篇文章是基于我对 Python stdlib 中“分工”的理解的推测,而不是基于事实的可参考信息。

您的问题源于这样一个事实:“C 结构”和“二进制数据”往往可以互换使用,虽然在实践中是正确的,但从技术意义上来说是错误的。这struct文档也具有误导性:它声称适用于“C 结构”,而更好的描述是“二进制数据”,并带有一些关于 C 兼容性的免责声明。

从根本上来说,struct, array and ctypes 做不同的事情. struct处理将 Python 值转换为二进制内存格式。array处理有效存储大量值的问题。ctypes与 C 打交道language(*)。功能上的重叠源于这样一个事实:对于 C 来说,“二进制内存格式”是本机的,并且“有效地存储值”is将它们打包到类似 C 的数组中。

您还会注意到struct让您轻松指定字节顺序,因为它以多种不同的打包方式处理二进制数据的打包和解包;而在ctypes获得非本机字节顺序比较困难,因为它使用字节顺序这是 C 原生的.

如果您的任务是读取二进制数据结构,则抽象级别会不断增加:

  1. 手动分割字节数组并转换部分int.from_bytes等等
  2. 使用格式字符串描述数据并使用struct一口气拆开包装
  3. 使用像这样的库构造 https://construct.readthedocs.io/en/latest/用逻辑术语声明性地描述结构。

ctypes甚至不知道这里,因为对于这个任务,使用ctypes几乎是通过一个往返不同的编程语言。事实上,它对于你的例子同样有效,这是偶然的;它之所以有效,是因为 C 本身就适合表达打包二进制数据的多种方式。但是,例如,如果您的结构是混合字节序,则很难用ctypes。另一个例子是半精度浮点数,它没有 C 等效项(请参见here https://docs.python.org/3/library/struct.html#format-characters).

从这个意义上来说,这也是很有道理的ctypes use struct- 毕竟,“打包和解包二进制数据”是“与 C 接口”的子任务。

另一方面,这对于struct to use ctypes:就像使用email字符编码转换库,因为这是电子邮件库可以完成的任务。

(*) 嗯,基本上。更准确地说,类似于“基于 C 的环境”,即现代计算机如何由于与 C 作为主要系统语言的共同进化而在低级别上工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python:结构体和数组与 ctypes 中的类似功能 的相关文章