Python:惰性字符串解码

时间:2009-11-01 00:49:49

标签: python lazy-evaluation

我正在编写一个解析器,并且有很多文本需要解码,但我的大多数用户只关心所有数据中的几个字段。所以我只想在用户实际使用某些数据时进行解码。这是一个很好的方法吗?

class LazyString(str):
    def __init__(self, v) :
        self.value = v
    def __str__(self) :
        r = ""
        s = self.value
        for i in xrange(0, len(s), 2) :
            r += chr(int(s[i:i+2], 16))
        return r

def p_buffer(p):
    """buffer : HASH chars"""
    p[0] = LazyString(p[2])

这是我需要覆盖的唯一方法吗?

5 个答案:

答案 0 :(得分:2)

我不确定如何实现字符串子类在这里有很多好处。在我看来,如果你正在处理一个包含数PB数据的流,那么每当你创建一个你不需要的对象时,你就已经丢失了游戏。您的首要任务应该是尽可能多地忽略输入。

你当然可以构建一个类似字符串的类:

class mystr(str):
    def __init__(self, value):
        self.value = value
        self._decoded = None
    @property
    def decoded(self):
        if self._decoded == None:
            self._decoded = self.value.decode("hex")
            return self._decoded
    def __repr__(self):
        return self.decoded
    def __len__(self):
        return len(self.decoded)
    def __getitem__(self, i):
        return self.decoded.__getitem__(i)
    def __getslice__(self, i, j):
        return self.decoded.__getslice__(i, j)

等等。这样做的一个奇怪的事情是,如果你继承str,那么你将没有显式实现的每个方法都将被调用传递给构造函数的值:

>>> s = mystr('a0a1a2')
>>> s
 ¡¢
>>> len(s)
3
>>> s.capitalize()
'A0a1a2'

答案 1 :(得分:1)

我在代码中看不到任何关于延迟评估的内容。您仅使用xrange这一事实意味着将根据需要生成从0len(s)的整数列表。无论如何,整个字符串r将在字符串转换期间被解码。

在Python中实现延迟序列的最佳方法是使用generators。你可以尝试这样的事情:

def lazy(v):
    for i in xrange(0, len(v), 2):
        yield int(v[i:i+2], 16)

list(lazy("0a0a0f"))
Out: [10, 10, 15]

答案 2 :(得分:0)

你正在做的事情已经内置:

s =  "i am a string!".encode('hex')
# what you do
r = ""
for i in xrange(0, len(s), 2) :
    r += chr(int(s[i:i+2], 16))
# but decoding is builtin
print r==s.decode('hex') # => True

正如您所看到的,整个解码都是s.decode('hex')

但“懒惰”解码听起来像对我来说过早优化。你需要几千兆字节的数据才能注意到它。尝试分析,.decode比旧代码快50倍。

也许你想要这样的事情:

class DB(object): # dunno what data it is ;)
    def __init__(self, data):
        self.data = data
        self.decoded = {} # maybe cache if the field data is long
    def __getitem__(self, name):
        try:
            return self.decoded[name]
        except KeyError:
            # this copies the fields data
            self.decoded[name] = ret = self.data[ self._get_field_slice( name ) ].decode('hex')
            return ret
    def _get_field_slice(self, name):
        # find out what part to decode, return the index in the data
        return slice( ... )

db = DB(encoded_data)    
print db["some_field"] # find out where the field is, get its data and decode it

答案 3 :(得分:0)

您需要覆盖的方法实际上取决于计划如何使用新的字符串类型。

但是你基于str的类型对我来说有点怀疑,你是否考虑过str的实现来检查它是否具有你在value中设置的__init__()属性?执行dir(str)并不表示str上存在任何此类属性。在这种情况下,普通的str方法根本不会对你的数据进行操作,我怀疑这是你想要的效果,否则会有什么优势。

除非您有非常具体的要求,否则无论如何,子类基础数据类型都有点奇怪。对于你想要的懒惰评估,你可能更擅长创建包含字符串而不是子类化str的类,并编写客户端代码以使用该类。然后,您可以通过多种方式自由添加您想要的即时评估,在此演示文稿中可以找到使用描述符协议的示例:Python's Object Model(搜索“class Jit(object)”以获取相关部分)

答案 4 :(得分:0)

问题不完整,答案取决于您使用的编码细节。

比方说,如果你将一个字符串列表编码为pascal字符串(即以字符串长度编码为固定大小的整数),并且你想要从列表中读取第100个字符串,你可以寻求()转发前99个字符串中的每一个都没有读取它们的内容。如果字符串很大,这将获得一些性能提升。

如果,OTOH,你将一个字符串列表编码为连接的0终止的stirngs,你必须读取所有字节,直到第100个。

另外,你说的是一些“字段”,但你的例子看起来完全不同。