如何覆盖通过ctypes _fields_创建的(可能是多维)数组的__setitem__方法?

时间:2012-05-23 20:08:38

标签: python ctypes

我在Python 3.2.2中使用ctypes来封装一些C数据结构。最终目标是能够在结构的数据时有一个包装C结构通知的对象 内容已被修改。

代表性代码:

from ctypes import *

class Comm(Structure):
    def __init__(self):
        self.attributes_updated = False

    def __setattr__(self, name, value):
        super(Comm, self).__setattr__('attributes_updated', True)
        super(Comm, self).__setattr__(name, value)


class MyCStruct(Comm):
    _fields_ = [('number', c_int),
                ('array', c_int*5)]

    def __init__(self):
        Comm.__init__(self)

这适用于任何简单的数据属性,例如'number'。

>>> s = MyCStruct()
>>> s.attributes_updated
False
>>> s.value = 123
>>> s.attributes_updated
True

由于未通过索引表示法访问__setattr__进行访问 array属性,因此我希望覆盖作为数组的C结构成员的__setitem__属性。据推测,此时我需要将一个引用包含在包含对象中,以便可以更改包含对象的attributes_updated变量,但是我还没有达到能够捕获对数组的访问的程度我可以方便地访问简单属性的属性。有没有办法在ctypes通过_fields_变量创建的可索引对象上执行此操作?是否可以覆盖__setitem__上的s.array?可能有更好的方法来做这件事吗?

理想情况下,会发生这种情况:

>>> s = MyCStruct()
>>> s.attributes_updated
False
>>> s.array[2] = 456
>>> s.attributes_updated
True

编辑后续问题:

多维数组怎么样?

class MyCStruct(Comm):
    _fields_ = [('number', c_int),
                ('array', (c_int*5)*2]

我错误地预期下面的答案,对于单维数组来说,它可以为任意嵌套的数组做出相同的工作。应该有一种方法来递归生成代理对象,为具有多个维度的数组做同样的事情,是吗?语法让我感到厌烦。

1 个答案:

答案 0 :(得分:1)

我认为一个很好的解决方案是返回代理对象而不是数组,然后可以处理对数组的访问。可能是这样的:

from ctypes import *

class ArrayProxy(object):
    def __init__(self, array, struct):
        self.array = array
        self.struct = struct

    def __setitem__(self, i, val):
        self.array[i] = val
        self.struct.attributes_updated = True

    def __getitem__(self, i):
        item = self.array[i]
        if issubclass(type(item), Array):
            # handle multidimensional arrays
            return ArrayProxy(item, self.struct)
        return item

class Comm(Structure):
    def __init__(self):
        self.attributes_updated = False

    def __setattr__(self, name, value):
        super(Comm, self).__setattr__('attributes_updated', True)
        super(Comm, self).__setattr__(name, value)

    def __getattribute__(self, name):
        attr = super(Comm, self).__getattribute__(name)
        if issubclass(type(attr), Array):
            return ArrayProxy(attr, self)
        return attr


class MyCStruct(Comm):
    _fields_ = [('number', c_int),
                ('array', c_int*5),
                ('multiarray', c_int*2*1),]

    def __init__(self):
        Comm.__init__(self)

s = MyCStruct()
print s.array
# <__main__.ArrayProxy object at 0x1b1f3d0> 
print s.attributes_updated
# False
s.array[0] = 1
print s.attributes_updated
# True

s2 = MyCStruct()
s2.multiarray[0][0] = 1
print s2.attributes_updated
# True