【问题标题】:Dynamically create Ctypes in Python在 Python 中动态创建 Ctypes
【发布时间】:2018-01-24 01:34:37
【问题描述】:

我有一个从中读取的文件,其中包含在单独项目中使用的 ctypes 定义。我可以读取文件并获取在 Python 中创建我想要的 ctype 所需的所有必要信息,例如名称、字段、位域、ctype 基类(结构、联合、枚举等)和包。

我希望能够根据上述信息创建一个 ctype 类。我还希望这些 ctypes 是可腌制的。

我目前有两个解决方案,我觉得这两个都是 hack。

解决方案 1

手动或使用 Jinja2 之类的工具以适当的 ctype 格式生成 Python 代码对象,然后评估 Python 代码对象。

此解决方案有使用 eval 的缺点。我总是尽量远离 eval,我觉得这不是一个使用它的好地方。

解决方案 2

在函数中动态创建 ctype,如下所示:

from ctypes import Structure

def create_ctype_class(name, base, fields, pack):
    class CtypesStruct(base):
        _fields_ = fields
        _pack_ = pack
    CtypesStruct.__name__ = name
    return CtypesStruct

ctype = create_ctype_class('ctype_struct_name', ctypes.Structure,
                           [('field1', ctypes.c_uint8)], 4)

这个解决方案还不错,但是设置类的名字很丑,而且类型不能pickle。

有没有更好的方法来创建动态 ctype 类?

注意:我使用的是 Python 2.7

【问题讨论】:

  • 您可以使用通用函数,例如create_type = lambda name, *bases, **attrs: type(name, bases, attrs)。然后使用ctype = create_type('ctype', Structure, _fields_=[('field1', c_uint8)], _pack_=4)
  • 对于酸洗,模块中设置的名称必须与类名一致。同样对于数组类型,子类Array 并设置_type__length_,不幸的是,这不如使用ctype * length 方便。此外,ctypes 默认__reduce__ 不会pickle 任何指针类型的实例,例如c_char_p,或包含一个或多个指针的聚合。
  • 如果我将_type__length_ 作为属性,您的第一条评论是否仍然有效?我正在开发一种适用于这些类的 reduce 方法。
  • 解决方案 2 对我来说效果很好。给那里的菜鸟(像我一样)'create_ctype_class() 的一个注释返回类本身,而不是它的实例。所以你必须my_ctype_class = create_ctype_class(...) 然后my_ctype = my_ctype_class() 才能实际使用它。

标签: python dynamic ctypes


【解决方案1】:

解决方案 2 可能是您更好的选择,但如果您也在静态编写此类类,您可能希望使用元类来删除其中一些代码的重复数据。如果您需要您的对象是可腌制的,那么您将需要一种从可腌制对象重建它们的方法。一旦你实现了这样的机制,你可以通过a __reduce__() method 让pickle 模块知道它。

【讨论】:

  • 好的。我想我必须弄清楚如何为这些类设置 __reduce__() 方法。我已经摆弄了很长一段时间。
  • Here's a simple __reduce__() I wrote。该类已经有了序列化机制,但它与__getstate__()/__setstate__()不直接兼容,所以我只是将它连接到__reduce__()
  • 没有必要覆盖__reduce__,除非您需要自定义支持来处理指针类型。否则非指针数据类型已经有一个工作的__reduce____setstate__。此外,在这种情况下,弄乱元类比它的价值更麻烦,因为 ctypes 使用它自己的元类,您必须对其进行子类化,例如type(ctypes.Structure)type(ctypes.Union)type(ctypes.Array)type(ctypes._SimpleCData)type(ctypes._Pointer)type(ctypes._CFuncPtr)
  • @eryksun - 我不知道 ctypes 能够被腌制。我正在解析的 ctype 中没有指针。我仍然严格执行__reduce__(),以使这些类的动态属性与stackoverflow.com/a/11493777/3221505的答案类似
【解决方案2】:

我会选择解决方案 1 的变体。不是评估代码,而是创建一个带有 __init__.py 的目录(即一个包),将其添加到您的 sys.path 并写出一个包含所有类。然后你可以从一个稳定的命名空间中导入它们,这将使pickle 更快乐。

您可以获取输出并将其添加到应用程序的源代码中,也可以动态重新创建它并在运行时将其缓存在目标机器上。

pywin32 使用类似的方法来缓存从 ActiveX 接口生成的类。

【讨论】:

  • 除非您能保证模块也将存在,并且名称和内容完全相同,否则不会干净地解酸,解酸机(可能与酸洗机不同)。
  • 当然可以,但是可以将结果添加到源代码控制中,或者您可以在目标机器上动态重新创建它。
  • 这很好地解决了我的问题。我没有提到这一点,但我在生成这些 ctype 的 XML 文件中至少有一千个或更多 ctypes。这个过程会经常发生,所以我不希望它花费超过 5-10 秒,目前使用解决方案 2 的修改版本运行它大约需要 5 秒。我认为这个解决方案不能满足这个要求,但我没有在我原来的问题中指定。
  • @David:我不知道pickle 提供了这种动态娱乐的任何钩子。我想你可以在__init__.py 期间重新创建或添加一个导入钩子,但恕我直言,这些都很丑。
  • 我不会挂钩 pickle - 只需在应用初始化期间设置它,然后再调用 pickle。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-14
  • 2012-06-11
  • 2018-04-15
  • 1970-01-01
相关资源
最近更新 更多