Python字符串操作 - 性能问题

bbe*_*mir 6 python string performance

我有以下代码,我在我的应用程序中执行大约200万次来解析那么多记录.这部分似乎是瓶颈,我想知道是否有人可以通过建议一些可以使这些简单的字符串操作更快的漂亮技巧来帮助我.

try:
    data = []
    start = 0
    end = 0
    for info in self.Columns():
        end = start + (info.columnLength)
        slice = line[start:end]
        if slice == '' or len(slice) != info.columnLength:
            raise 'Wrong Input'
        if info.hasSignage:
            if(slice[0:1].strip() != '+' and slice[0:1].strip() != '-'):
                raise 'Wrong Input'
        if not info.skipColumn:
            data.append(slice)
        start = end 
    parsedLine = data
except:
    parsedLine = False
Run Code Online (Sandbox Code Playgroud)

ste*_*eha 1

编辑:我稍微改变一下这个答案。我将在下面留下原始答案。

在我的另一个答案中,我评论说最好的办法是找到一个内置的 Python 模块来完成解包。我想不出一个,但也许我应该在谷歌上搜索一个。@John Machin 提供了一个答案,展示了如何做到这一点:使用 Pythonstruct模块。由于它是用 C 编写的,因此它应该比我的纯 Python 解决方案更快。(我没有实际测量过任何东西,所以这是一个猜测。)

我确实同意原始代码中的逻辑是“非Pythonic”。返回哨兵值并不是最好的;最好返回一个有效值或引发异常。另一种方法是返回有效值列表以及另一个无效值列表。由于@John Machin 提供了生成有效值的代码,我想我应该在这里编写一个返回两个列表的版本。

注意:也许最好的答案是采用@John Machin 的答案并修改它以将无效值保存到文件中以供以后查看。他的答案一次产生一个答案,因此不需要构建一大堆已解析记录;将坏行保存到磁盘意味着无需构建可能很大的坏行列表。

import struct

def parse_records(self):
    """
    returns a tuple: (good, bad)
    good is a list of valid records (as tuples)
    bad is a list of tuples: (line_num, line, err)
    """

    cols = self.Columns()
    unpack_fmt = ""
    sign_checks = []
    start = 0
    for colx, info in enumerate(cols, 1):
        clen = info.columnLength
        if clen < 1:
            raise ValueError("Column %d: Bad columnLength %r" % (colx, clen))
        if info.skipColumn:
            unpack_fmt += str(clen) + "x"
        else:
            unpack_fmt += str(clen) + "s"
            if info.hasSignage:
                sign_checks.append(start)
        start += clen
    expected_len = start
    unpack = struct.Struct(unpack_fmt).unpack

    good = []
    bad = []
    for line_num, line in enumerate(self.whatever_the_list_of_lines_is, 1):
        if len(line) != expected_len:
            bad.append((line_num, line, "bad length"))
            continue
        if not all(line[i] in '+-' for i in sign_checks):
            bad.append((line_num, line, "sign check failed"))
            continue
        good.append(unpack(line))

    return good, bad
Run Code Online (Sandbox Code Playgroud)

self.Columns()原始答案文本:如果所有记录中的信息都相同,那么这个答案应该会快得多。我们对信息进行一次处理self.Columns(),并构建几个列表,其中包含处理记录所需的内容。

这段代码展示了如何计算parsedList,但实际上并没有产生它或返​​回它或用它做任何事情。显然你需要改变这一点。

def parse_records(self):
    cols = self.Columns()

    slices = []
    sign_checks = []
    start = 0
    for info in cols:
        if info.columnLength < 1:
            raise ValueError, "bad columnLength"
        end = start + info.columnLength
        if not info.skipColumn:
            tup = (start, end)
            slices.append(tup)   
            if info.hasSignage:
                sign_checks.append(start)

    expected_len = end # or use (end - 1) to not count a newline

    try:
        for line in self.whatever_the_list_of_lines_is:
            if len(line) != expected_len:
                raise ValueError, "wrong length"
            if not all(line[i] in '+-' for i in sign_checks):
                raise ValueError, "wrong input"
            parsedLine = [line[s:e] for s, e in slices]

    except ValueError:
        parsedLine = False
Run Code Online (Sandbox Code Playgroud)