2013年8月18日 星期日

Struct in Python and Heritance

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.

沒有留言:

張貼留言

歡迎留言討論與指教