2009-05-21 8 views
6

ここでは、たくさんのクラスが自分自身のバイナリ表現(C++で読まれる)をどのように返すかを考案した例を示します。もっとPythonicのバイナリへの変換?

def to_binary(self): 
    'Return the binary representation as a string.' 
    data = [] 

    # Binary version number. 
    data.append(struct.pack('<I', [2])) 

    # Image size. 
    data.append(struct.pack('<II', *self.image.size)) 

    # Attribute count. 
    data.append(struct.pack('<I', len(self.attributes))) 

    # Attributes. 
    for attribute in self.attributes: 

     # Id. 
     data.append(struct.pack('<I', attribute.id)) 

     # Type. 
     data.append(struct.pack('<H', attribute.type)) 

     # Extra Type.   
     if attribute.type == 0: 
      data.append(struct.pack('<I', attribute.typeEx)) 

    return ''.join(data) 

は私が嫌いなもの:

  • すべての行は、行のユニークな部分から邪魔、data.append(struct.pack(から始まります。
  • バイトオーダー('<')が何度も繰り返されます。
  • ボイラープレート''.join(data)を返すことを覚えておく必要があります。

    • 書式指定子は、属性名の近くに表示されます。私は好き

    。たとえば、self.image.sizeが2つの符号なし整数として書き出されることは容易にわかります。

  • 行は(ほとんど)独立しています。たとえば、「属性」からIDフィールドを削除するには、複数行のコードを操作する必要はありません。

これを行うには、より読みやすい/ pythonの方法がありますか?

答えて

4
from StringIO import StringIO 
import struct 

class BinaryIO(StringIO): 
    def writepack(self, fmt, *values): 
     self.write(struct.pack('<' + fmt, *values)) 

def to_binary_example(): 
    data = BinaryIO() 
    data.writepack('I', 42) 
    data.writepack('II', 1, 2) 
    return data.getvalue() 
4

あなたのデータにはある種のdeclarative syntaxを実装しようとすることができます。以下のようなものをもたらすことができる

どちら:

class Image(SomeClassWithMetamagic): 
    type = PackedValue(2) 
    attribute = PackedValue('attributes') # accessed via self.__dict__ 

#or using decorators 
    @pack("<II") 
    def get_size(): 
     pass 

#and a generic function in the Superclass 
    def get_packed(): 
     stuff 

等...

他の例としては、SQLAlchemyののdeclarative_base、ToscaWidgetsとsprox

+0

宣言的な構文は、シリアライゼーションを構築するために複雑なプログラムロジックを必要としない場合(つまり、ifとforsがたくさんある場合)に適しています。私は宣言的アプローチを使用して、シリアライズ、デシリアライゼーション、およびバイナリファイル形式のために自動的に生成されたドキュメントを指定しました。 –

0

だろうあなたには定型をラップするようにコードをリファクタリングできクラス。ような何か:

def to_binary(self): 
    'Return the binary representation as a string.' 
    binary = BinaryWrapper() 

    # Binary version number. 
    binary.pack('<I', [2]) 

    # alternatively, you can pass an array 
    stuff = [ 
     ('<II', *self.image.size),   # Image size. 
     ('<I', len(self.attributes)),  # Attribute count 
    ] 
    binary.pack_all(stuff) 

    return binary.get_packed() 
1
def to_binary(self): 
    struct_i_pack = struct.Struct('<I').pack 
    struct_ii_pack = struct.Struct('<II').pack 
    struct_h_pack = struct.Struct('<H').pack 
    struct_ih_pack = struct.Struct('<IH').pack 
    struct_ihi_pack = struct.Struct('<IHI').pack 

    return ''.join([ 
     struct_i_pack(2), 
     struct_ii_pack(*self.image.size), 
     struct_i_pack(len(self.attributes)), 
     ''.join([ 
      struct_ih_pack(a.id, a.type) if a.type else struct_ihi_pack(a.id, a.type, a.typeEx) 
      for a in attributes 
     ]) 
    ]) 
0

最悪の問題は、出力を読み取るためにC++に対応するコードが必要だということです。読解と書込みの両方のコードを機械的に共通の仕様から派生させたり、共通の仕様を使用することを合理的に手配できますか?どのように進むかについては、PythonほどC++のニーズにもよります。

0

まだ読み取り可能にしながら、あなたは簡単にこのように繰り返しを取り除くことができます。

def to_binary(self):  
    output = struct.pack(
     '<IIII', 2, self.image.size[0], self.image.size[1], len(self.attributes) 
    ) 
    return output + ''.join(
     struct.pack('<IHI', attribute.id, attribute.type, attribute.typeEx) 
     for attribute in self.attributes 
    ) 
+0

私はあなたが "attribute.type == 0の場合:" –

2

あなただけのよりよい構文をしたい場合は、発電機/デコレータを乱用することができます

from functools import wraps  

def packed(g): 
    '''a decorator that packs the list data items 
    that is generated by the decorated function 
    ''' 
    @wraps(g) 
    def wrapper(*p, **kw): 
    data = [] 
    for params in g(*p, **kw): 
     fmt = params[0] 
     fields = params[1:] 
     data.append(struct.pack('<'+fmt, *fields)) 
    return ''.join(data)  
    return wrapper 

@packed 
def as_binary(self): 
    '''just |yield|s the data items that should be packed 
    by the decorator 
    ''' 
    yield 'I', [2] 
    yield 'II', self.image.size[0], self.image.size[1] 
    yield 'I', len(self.attributes) 

    for attribute in self.attributes: 
    yield 'I', attribute.id 
    yield 'H', attribute.type 
    if attribute.type == 0: 
     yield 'I', attribute.typeEx 

基本的に、これは使用していますジェネレータは "モナド"を実装しています。これは通常、Haskellのような関数型言語で見られる抽象です。これらの値を組み合わせる方法を決定するコードからいくつかの値の生成を分離します。それは機能的なプログラミングのアプローチ "pythonic"ですが、私はそれが読みやすさを向上させると思います。

+0

+1を見逃したと思います。 文字通りまったく同じソリューションを投稿するのに数秒しかかかりませんでした。可読性を向上させるための小さな改良点は、データ型文字列を関数にカプセル化して 'I'を生成することです。属性.idは、yield UInt(attribute.id)になります。 –

2

おおよそprotocol buffers Googleの広範なクロス言語フォーマットとデータ共有プロトコル。

関連する問題