这是一个人为的例子,说明我们的很多类如何返回自己的二进制表示(由C ++读取)。
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
被写成两个未签名的整数。有更可读/ pythonic的方法吗?
答案 0 :(得分: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
答案 1 :(得分: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()
答案 2 :(得分: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
基本上,它使用生成器来实现“monad”,这是一种通常在Haskell等函数式语言中找到的抽象。它将某些值的生成与决定如何将这些值组合在一起的代码分开。它更像是一种函数式编程方法,然后是“pythonic”,但我认为它提高了可读性。
答案 3 :(得分:2)
protocol buffers谷歌广泛的跨语言格式和共享数据协议怎么样?
答案 4 :(得分: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
])
])
答案 5 :(得分: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()
答案 6 :(得分:0)
最糟糕的问题是你需要在C ++中使用相应的代码来读取输出。您是否可以合理安排将读写代码机械地衍生自或使用通用规范?如何解决这个问题取决于你的C ++需求和Python一样多。
答案 7 :(得分: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
)