Python字符串操作——性能问题
我在应用程序中执行了大约200万次以下代码来解析这么多记录。这一部分似乎是瓶颈,我想知道是否有人可以帮助我提出一些漂亮的技巧,使这些简单的字符串操作更快Python字符串操作——性能问题,python,string,performance,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 == ''
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
不要每次通过此循环计算
start
和end
在使用self.Columns()
之前,只计算它们一次(不管是什么。如果'Columns'是带有静态值的类,那就太傻了。如果它是一个名称以大写字母开头的函数,那就很混乱。)
if slice==''或len(slice)!=信息.columnLength
仅当行与列
所需的总大小相比太短时才会发生。检查一次,在回路外
slice[0:1].strip()!='+'代码>确定看起来像.startswith()
if not info.skipColumn
。在开始循环之前应用此过滤器。将这些从“代码>自我”栏中删除()/代码> < /p> < p>我首先考虑的是“代码>切片=行[开始:结束] < /代码>。切片创建新实例;您可以尝试避免显式构造行[start:end]
,并手动检查其内容
你为什么要做切片[0:1]
?这将产生一个包含单个片段项的子序列(不应该吗?),因此可以更有效地检查它 怎么样(使用一些类来创建一个可执行示例):
编辑:
当切片的长度为0时,切片[0]不存在,因此必须首先检查如果collength==0
编辑2:
您在许多行中使用这段代码,但列信息不会更改,对吗?那就让你,去,
- 预先计算每个柱的起点列表(不再需要计算起点、终点)
- 提前知道开始和结束,.Columns()只需要返回未被跳过且columnlength>0的列(或者您真的需要在每一行为length==0进行输入吗??)
- 每条线的下颌骨长度已知且相等,或者每条线的下颌骨长度相等,可以在绕过列信息之前进行检查
编辑3:
我想知道如果您使用'skipColumn',您将如何知道什么数据索引属于哪个列……我想告诉您使用某种内置Python功能来拆分字符串,但我想不出一个。所以我只剩下尽量减少代码量了
完成后,end
应该指向字符串的末尾;如果是这种情况,则所有.columnLength
值都必须正常。(除非其中一个是否定的或什么的!)
因为它引用了self
,所以它必须是来自成员函数的剪报。因此,您不必引发异常,只需返回False
即可提前退出函数并返回错误标志。但是我喜欢将except
子句更改为不再捕获异常,并获取堆栈跟踪以确定问题的来源的调试潜力
@Remi在'+-'中使用了slice[0],其中我使用了slice.startswith(('+','-)
。我想我更喜欢@Remi的代码,但我的代码没有改变,只是为了给大家展示一种不同的方式。.startswith()
方法适用于长度大于1的字符串,但由于这只是长度为1的字符串,因此简洁的解决方案有效
try:
line = line.strip('\n')
data = []
start = 0
for info in self.Columns():
end = start + info.columnLength
slice = line[start:end]
if info.hasSignage and not slice.startswith(('+', '-')):
raise ValueError, "wrong input"
if not info.skipColumn:
data.append(slice)
start = end
if end - 1 != len(line):
raise ValueError, "bad .columnLength"
parsedLine = data
except ValueError:
parsedLine = False
编辑:我对这个答案做了一些修改。我将在下面留下原始答案
在我的另一个回答中,我评论说最好的办法是找到一个内置的Python模块来进行解包。我想不出一个,但也许我应该在谷歌上搜索一个@John Machin提供了一个说明如何实现的答案:使用Pythonstruct
模块。因为它是用C编写的,所以应该比我的纯Python解决方案快。(我实际上没有测量任何东西,所以这只是猜测。)
我同意原始代码中的逻辑是“非Pythonic的”。返回sentinel值不是最好的;最好返回有效值或引发异常。另一种方法是返回一个有效值列表,再加上另一个无效值列表。既然@johnmachin提供了生成有效值的代码,我想我应该在这里编写一个返回两个列表的版本
注意:也许最好的答案是采用@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
在raise
语句中有不同的错误消息是没有意义的;他们从未见过。嘘#3
更新:这里有一个建议的改进,它使用struct.unpack
快速划分输入行。它还演示了更好的异常处理,假设代码编写者也在运行它,并且在第一个错误时停止是可以接受的。另一个问题是,一个健壮的实现可以为用户用户记录所有行的所有列中的所有错误。请注意,通常情况下,每列的错误检查会更广泛,例如,检查前导符号,但不检查列是否包含有效数字似乎有点奇怪
import struct
def unpacked_records(self):
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
for linex, line in enumerate(self.whatever_the_list_of_lines_is, 1):
if len(line) != expected_len:
raise ValueError(
"Line %d: Actual length %d, expected %d"
% (linex, len(line), expected_len))
if not all(line[i] in '+-' for i in sign_checks):
raise ValueError("Line %d: At least one column fails sign check" % linex)
yield unpack(line) # a tuple
导入结构
def未打包_记录(自我):
cols=self.Columns()
解包_fmt=“”
签署支票=[]
开始=0
对于colx,枚举中的信息(cols,1):
clen=info.columnLength
如果clen<1:
raise VALUERROR(“列%d:错误的列长度%r”%(colx,clen))
如果info.skipColumn:
解包_fmt+=str(clen)+“x”
其他:
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
def fubarise(data):
try:
if nasty(data):
raise ValueError("Look, Ma, I'm doing a big fat GOTO ...") # sheesh #1
more_of_the_same()
parsed_line = data
except ValueError:
parsed_line = False
# so it can be a "data" or False -- sheesh #2
return parsed_line
import struct
def unpacked_records(self):
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
for linex, line in enumerate(self.whatever_the_list_of_lines_is, 1):
if len(line) != expected_len:
raise ValueError(
"Line %d: Actual length %d, expected %d"
% (linex, len(line), expected_len))
if not all(line[i] in '+-' for i in sign_checks):
raise ValueError("Line %d: At least one column fails sign check" % linex)
yield unpack(line) # a tuple