How does one override the __setitem__ method for (possibly multidimensional) arrays created via ctypes _fields_?
Question: I am using ctypes in Python 3.2.2 to encapsulate some C data structures. The
ultimate goal is to be able to have an object that wraps a C structure notice
when the structure's data contents have been modified.

Representative code:

    
    
    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)
    

This works great for any simple data attribute like 'number'.

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

Since `__setattr__` is not invoked for accesses via index notation to the
`array` attribute, I would thus like to override the `__setitem__` attribute
for those members of the C struct that are arrays. Presumably, at that point I
would need to include a reference back to the containing object so that the
containing object's `attributes_updated` variable could be changed, but I've
not gotten to the point where I am able to trap accesses to array attributes
in the convenient way that I can trap accesses to simple attributes. Is there
a way to do this on the indexable objects that ctypes creates via the
`_fields_` variable? Is it possible to override `__setitem__` on `s.array`?
Might there be a better way to go about doing this?

Ideally, this would happen:

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

Edit for follow-up question:

How about a multidimensional array?

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

I mistakenly expected the answer below, which works brilliantly for single
dimensional arrays to do the same for arbitrarily nested ones. There should be
a way to recursively spawn proxy objects to do the same thing for arrays with
more than one dimension, yes? The syntax escapes me.


Answer: I think a nice solution would be to return a proxy object instead of the
array, which can then handle the access to the array. Could be something like
this:

    
    
    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
    

