2009-05-21 8 views
6

Voici un exemple inventé de la façon dont beaucoup de nos classes renvoient des représentations binaires (à lire en C++) d'elles-mêmes.Plus de conversion Pythonic en binaire?

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) 

Ce que je n'aime pas:

  • Chaque ligne commence par data.append(struct.pack(, distrayant de la partie unique de la ligne.
  • L'ordre des octets ('<') est répété encore et encore. Vous devez vous rappeler de renvoyer la plaque chauffante ''.join(data).

Ce que j'aime:

  • Les indicateurs de format apparaissent à côté du nom d'attribut. Par exemple, il est facile de voir que self.image.size est écrit comme deux ints non signés.
  • Les lignes sont (pour la plupart) indépendantes. Par exemple, pour supprimer le champ Id d'un 'attribut', vous n'avez pas besoin de toucher plus d'une ligne de code.

Existe-t-il une façon plus lisible/pythonique de faire cela?

Répondre

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

Vous pouvez essayer d'implémenter une sorte de declarative syntax pour vos données.

Ce qui peut se traduire par quelque chose comme:

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 

etc ...

D'autres exemples seraient declarative_base de sqlalchemy, ToscaWidgets et Sprox

+0

La syntaxe déclarative est bonne si vous n'avez pas besoin d'une logique programmatique complexe pour générer la sérialisation (c'est-à-dire beaucoup d'ifs et de fors). J'ai utilisé l'approche déclarative pour spécifier la sérialisation, la désérialisation et la documentation générée automatiquement en une fois pour un format de fichier binaire. –

0

Vous pouvez factoriser votre code pour envelopper dans un passe-partout classe. Quelque chose comme:

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

Le pire problème est que vous avez besoin du code correspondant en C++ pour lire la sortie. Pouvez-vous raisonnablement faire en sorte que le code de lecture et d'écriture dérive mécaniquement ou utilise une spécification commune? Comment y parvenir dépend de vos besoins C++ autant que Python.

0

Vous pouvez vous débarrasser de la répétition tout en moins lisible facilement comme ceci:

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

Je pense que vous avez manqué "if attribute.type == 0:" –

2

Si vous voulez juste plus belle syntaxe, vous pouvez abuser des générateurs/décorateurs:

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 

Fondamentalement cette utilisations le générateur pour mettre en œuvre une "monade", une abstraction habituellement trouvée dans des langages fonctionnels comme Haskell. Il sépare la génération de certaines valeurs du code qui décide comment combiner ces valeurs ensemble. C'est plus une approche de programmation fonctionnelle que "pythonic", mais je pense que cela améliore la lisibilité.

+0

+1. J'étais littéralement à quelques secondes de poster exactement la même solution. Une petite amélioration pour améliorer la lisibilité consisterait à encapsuler la chaîne de type de données dans une fonction, donc céder 'I', attribute.id devient yield UInt (attribut.id). –

2

Que diriez-vous de protocol buffers le format de langue croisé de google et le protocole de partage de données.