13

I have a ctypes structure.

class S1 (ctypes.Structure):
    _fields_ = [
    ('A',     ctypes.c_uint16 * 10),
    ('B',     ctypes.c_uint32),
    ('C',     ctypes.c_uint32) ]

if I have X=S1(), I would like to return a dictionary out of this object: Example, if I do something like: Y = X.getdict() or Y = getdict(X), then Y might look like:

{ 'A': [1,2,3,4,5,6,7,8,9,0], 
  'B': 56,
  'C': 8986 }

Any help ?

G.A.
  • 1,443
  • 2
  • 22
  • 34
  • 1
    BTW, is there a reason you need a real `dict` object and can't just add `__getitem__()` etc methods to your structure classes? – llasram Sep 24 '10 at 18:31
  • In my case I wanted a dict of dicts with data about processes on a system...so it made more sense to put it in a dict of dicts – RobotHumans Mar 01 '12 at 12:22

3 Answers3

12

Probably something like this:

def getdict(struct):
    return dict((field, getattr(struct, field)) for field, _ in struct._fields_)

>>> x = S1()
>>> getdict(x)
{'A': <__main__.c_ushort_Array_10 object at 0x100490680>, 'C': 0L, 'B': 0L}

As you can see, it works with numbers but it doesn't work as nicely with arrays -- you will have to take care of converting arrays to lists yourself. A more sophisticated version that tries to convert arrays is as follows:

def getdict(struct):
    result = {}
    for field, _ in struct._fields_:
         value = getattr(struct, field)
         # if the type is not a primitive and it evaluates to False ...
         if (type(value) not in [int, long, float, bool]) and not bool(value):
             # it's a null pointer
             value = None
         elif hasattr(value, "_length_") and hasattr(value, "_type_"):
             # Probably an array
             value = list(value)
         elif hasattr(value, "_fields_"):
             # Probably another struct
             value = getdict(value)
         result[field] = value
    return result

If you have numpy and want to be able to handle multidimensional C arrays, you should add import numpy as np and change:

 value = list(value)

to:

 value = np.ctypeslib.as_array(value).tolist()

This will give you a nested list.

Tamás
  • 47,239
  • 12
  • 105
  • 124
  • Nice ! Nearly there. I am still facing a new situation in which one ctype structure S1 has a field that is of another type S2. Wonder if that can be converted too. – G.A. Sep 24 '10 at 18:08
  • I've added another branch that tries to handle nested structs (assuming that structs can be recognised by their `_fields_` attribute). Not sure if this works, though, I haven't tested it. – Tamás Sep 24 '10 at 18:29
  • It works !! Thanks. I'm trying to see if I can do the reverse. That is, given a dictionary of values, set the values in a ctype (which may again have embedded subtypes). I stuck at the trying to find out which type is the embedded type. – G.A. Oct 04 '10 at 21:14
  • Came here looking for something very similar. @G.A., did ever get the reverse working? I think I have gotten a more generic getdict (nested Structs, with arrays of builtins or Structs), and a generic getstruct both working, but I'm not sure if they're exactly optimized or the best way to do it. – Mark Dec 30 '11 at 23:31
  • @Mark: No, I did not get the reverse working. But I have moved on to other things for now. So I may not get to try my hand at the reverse conversion. – G.A. Mar 02 '12 at 01:29
  • @G.A. Ok, I will dig up my functions and post as a WIP answer. – Mark Mar 07 '12 at 01:22
  • Not that the first `if` clause will not detect `long`-types in python2.7 and earlier. So to get this working you should add `long` to the list of types. – jrast Dec 02 '15 at 17:22
  • Thanks, I've added `long` to the list of types there. – Tamás Dec 02 '15 at 20:32
2

A little bit more general purpose to handle double arrays, and arrays of structures, and bitfields.

def getdict(struct):
    result = {}
    #print struct
    def get_value(value):
         if (type(value) not in [int, long, float, bool]) and not bool(value):
             # it's a null pointer
             value = None
         elif hasattr(value, "_length_") and hasattr(value, "_type_"):
             # Probably an array
             #print value
             value = get_array(value)
         elif hasattr(value, "_fields_"):
             # Probably another struct
             value = getdict(value)
         return value
    def get_array(array):
        ar = []
        for value in array:
            value = get_value(value)
            ar.append(value)
        return ar
    for f  in struct._fields_:
         field = f[0]
         value = getattr(struct, field)
         # if the type is not a primitive and it evaluates to False ...
         value = get_value(value)
         result[field] = value
    return result
Sam P
  • 681
  • 5
  • 19
2

How about something like:

class S1(ctypes.Structure):
    _fields_ = [ ... ]

    def getdict(self):
        dict((f, getattr(self, f)) for f, _ in self._fields_)
llasram
  • 4,417
  • 28
  • 28