Python is an extendable programming language , as you know it supports Object-Oriented programming though it is not a strong type language. How do you write a class to support heritance if you want to use it as 'struct' type in C Programming ? I think this is an interesting question. I will demo an example that how I achieve it.
First, you need to study
'struct — Interpret strings as packed binary data' in Python manuals. It will tell you how to pack and unpack binary data.
Second, you have to study
'3.4.1 Basic customization' to know behavior of function '__new__'.
1 import string;
2 import struct;
3
4 class StructMeta(type):
5 __TYPE__ = ['x','c','b','B','?','h','H','i','I',
6 'l','L','q','Q','f','d','s','p','P'];
7 ___struct___ = '___struct___';
8 ___fields___ = '___fields___';
9 ___fmt___ = '___fmt___';
10
11 def __new__(cls, clsname, clsbases, clsdict):
12 """
13 This function will be recursive invoked when classes be loaded.
14 """
15 clsdict2 = dict();
16 fields = [];
17 format = [];
18 if(len(clsbases)>0):
19 # It will come here if only if cls has parent class!
20 parentFields = clsbases[0].__getattribute__(clsbases[0], StructMeta.___fields___);
21 for field in parentFields :
22 fields.append(field);
23
24 parentFormat = clsbases[0].__getattribute__(clsbases[0], StructMeta.___fmt___);
25 format.append(parentFormat);
26
27 for field in clsdict:
28 structDef = clsdict[field];
29 if(string.find(field, StructMeta.___struct___) < 0):
30 clsdict2[field] = structDef;
31 continue;
32 for fieldDef in structDef:
33 name = fieldDef[0];
34 fmt = fieldDef[1];
35 if(fmt not in StructMeta.__TYPE__):
36 raise Exception("Not support type '"+fmt+"' at " + name);
37 format.append(fmt);
38 defaultVal = fieldDef[2];
39 if(name in fields):
40 raise Exception("Redefinition '"+name+"' at "+ field);
41 fields.append(name);
42 clsdict2[name] = defaultVal;
43
44 clsdict2[StructMeta.___fmt___] = string.join(format,'');
45 clsdict2[StructMeta.___fields___] = fields;
46 return type.__new__(cls, clsname, clsbases, clsdict2);
47
48
49 class StructObject:
50 __metaclass__ = StructMeta;
51 ___endian___ = '!';
52 #@ native native
53 #= native standard
54 #< little-endian standard
55 #> big-endian standard
56 #! network (= big-endian) standard
57 """
58 It support define struct type
59 """
60 def __init__(self):
61 pass;
62
63 def setEndian(self, edian):
64 self.___endian___ = edian;
65
66 def unpack(self, buf):
67 values = struct.unpack(self.___endian___+ self.___fmt___, buf);
68 i = 0;
69 for field in self.___fields___:
70 self.__setattr__(field, values[i]);
71 i += 1;
72
73 class A(StructObject):
74 ___struct___A = (
75 ('field1', 'H', 0),
76 );
77
78 class B(A):
79 ___struct___B = (
80 ('field2', 'I', 0),
81 );
82
83 class C(B):
84 ___struct___C = (
85 ('field3', 'H', 0),
86 );
87
88 if __name__ == "__main__":
89
90 obj = C();
91 buf = '\x00\x02\x00\x00\x00\x04\x00\x06';
92 obj.unpack(buf);
93 print(obj.field1);
94 print(obj.field2);
95 print(obj.field3);
96
97 ############### Output ###################
98 # 2 #
99 # 4 #
100 # 6 #
101 ##########################################
As you see in output, class A, class B and class C have different fields then 'obj' is an instance of class C. Then we unpack binary data and we could access fields that defined in different class.The class C has fields which inherits from class B and class A.
What is that we need to be careful of this design ?
1. You need to maintain definition of fields by yourself.
2. Don't redefine same filed name. It doesn't support field hiden.
3. The '___struct___' will not be an actual field in an instance of class C.