2009-05-21 45 views
6

Đây là một ví dụ giả tạo về việc có bao nhiêu lớp của chúng ta trả về các biểu diễn nhị phân (được đọc bởi C++) của chính chúng.Chuyển đổi Pythonic thành nhị phân?

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) 

Những gì tôi không thích:

  • Mỗi dòng bắt đầu với data.append(struct.pack(, mất tập trung từ phần độc đáo của dòng.
  • Thứ tự byte ('<') được lặp đi lặp lại nhiều lần.
  • Bạn phải nhớ trả lại bản mẫu.

Những gì tôi thích:

  • Định dạng specifiers xuất hiện gần tên thuộc tính. Ví dụ: thật dễ dàng để thấy rằng self.image.size được viết thành hai int không dấu.
  • Các dòng (chủ yếu) độc lập. Ví dụ: Để xóa trường Id khỏi 'thuộc tính', bạn không phải chạm nhiều hơn một dòng mã.

Có cách nào dễ đọc hơn/có thể đọc được để thực hiện việc này không?

Trả lời

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

Bạn có thể thử triển khai một số loại declarative syntax cho dữ liệu của mình.

nào có thể dẫn đến một cái gì đó như:

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 

vv ...

ví dụ khác sẽ SQLAlchemy của declarative_base, ToscaWidgets và sprox

+0

Cú pháp khai báo là tốt nếu bạn không cần logic lập trình phức tạp để tạo tuần tự hóa (nghĩa là rất nhiều ifs và fors). Tôi đã sử dụng cách tiếp cận khai báo để xác định tuần tự hóa, deserialization và tài liệu được tạo tự động trong một lần cho một tệp nhị phân. –

0

Bạn có thể cấu trúc lại mã của bạn để quấn soạn sẵn trong một lớp học. Một cái gì đó như:

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

Vấn đề tồi tệ nhất là bạn không cần mã tương ứng trong C++ để đọc kết quả. Bạn có thể sắp xếp hợp lý để có cả mã đọc và viết bằng máy móc lấy từ hoặc sử dụng một đặc tả chung không? Làm thế nào để đi về điều đó phụ thuộc vào C++ của bạn cần nhiều như Python.

0

Bạn có thể thoát khỏi sự lặp lại trong khi vẫn là có thể đọc được một cách dễ dàng như thế này:

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

Tôi nghĩ bạn đã bỏ lỡ "nếu attribute.type == 0:" –

2

Nếu bạn chỉ muốn cú pháp đẹp hơn, bạn có thể lạm dụng máy phát điện/trang trí:

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 

Về cơ bản sử dụng này các máy phát điện để thực hiện một "monad", một trừu tượng thường được tìm thấy trong các ngôn ngữ chức năng như Haskell. Nó phân tách việc tạo ra một số giá trị từ mã quyết định cách kết hợp các giá trị này với nhau. Đó là một phương pháp tiếp cận lập trình chức năng hơn sau đó "pythonic", nhưng tôi nghĩ rằng nó cải thiện khả năng đọc.

+0

+1. Tôi chỉ mất vài giây để đăng chính xác giải pháp đó. Một cải tiến nhỏ để tăng cường khả năng đọc sẽ là gói gọn chuỗi dữ liệu trong một hàm để sinh ra 'I', attribute.id trở thành năng suất UInt (attribute.id). –

2

Làm thế nào về protocol buffers định dạng ngôn ngữ chéo rộng lớn của Google và giao thức chia sẻ dữ liệu.

Các vấn đề liên quan