有没有一种更优雅的方法可以将包含mpz值的文本文件读取到整数列表中



我有一个包含数字的文本文件,如下所示:

[mpz(0), mpz(0), mpz(0), mpz(0), mpz(4), mpz(54357303843626),...]

是否存在将其直接解析为整数列表的简单方法?目标数据类型是mpz整数还是普通python整数并不重要。

到目前为止,我尝试并工作的是纯解析(注意:目标数组y_val3需要提前用零初始化,因为它可能比文本文件中的列表大):

text_file = open("../prod_sum_copy.txt", "r")
content = text_file.read()[1:-1]
text_file.close()
content_list = content.split(",")
y_val3 = [0]*10000
print(content_list)
for idx, str in enumerate(content_list):
m = re.search('mpz(([0-9]+))', str)
y_val3[idx]=int(m.group(1))
print(y_val3)

尽管这种方法有效,但我不确定这是否是一种最佳实践,也不确定是否存在比简单解析更优雅的方法。

为了方便起见:这是GitHub上的原始文本文件。注意:这个文本文件可能会随着时间的推移而增长,这会带来性能和可伸缩性等方面的影响。

我试着从可读性和性能的角度来看一个更优雅的解决方案。

注意事项:

  • 这里发生了很多事情
  • 我没有原始文件,所以下面的数字与您在设备上获得的任何数字都不匹配
  • 有太多的工作要尝试并对所有不同的部分进行基准测试,所以我试图专注于几个最大的组件

下面的突破和时间似乎显示了几种方法的数量级差异,因此它们可能仍然可以用于衡量计算工作的水平。

我的第一种方法是尝试测量文件读/写添加到过程中的开销,这样我们就可以探索有多少计算工作只集中在数据处理步骤上。

为了做到这一点,我制作了一个函数,其中包括文件的读取和测量整个过程,端到端查看我的迷你示例文件花了多长时间。我用Jupyter笔记本上的%timeit做了这个。

然后,我将文件读取步骤分解为它自己的功能,然后仅在数据处理步骤中使用%timeit来帮助我们展示:

  • 在原始方法中,文件读取与数据处理使用了多少时间
  • 改进方法中的数据处理方法使用了多少时间

原始方法(在函数中)

import re
def original():
text_file = open("../prod_sum_copy.txt", "r")
content = text_file.read()[1:-1]
text_file.close()
content_list = content.split(",")
y_val3 = [0]*10000
for idx, element in enumerate(content_list):
m = re.search('mpz(([0-9]+))', element)
y_val3[idx]=int(m.group(1))
return y_val3

我想,我的短示例数据的很大一部分处理时间只是用来打开磁盘上的文件、将数据读入内存、关闭文件等的时间。

%timeit original()
140 µs ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

从数据处理方法中分离Readfile

这种方法包括对文件读取过程的微小改进。计时测试不包括文件读取过程,所以我们不知道这个小变化对整个过程的影响有多大。为了记录在案,我通过将读取过程封装在with上下文管理器(在后台处理关闭)中,消除了对.close()方法的手动调用,因为这是Python读取文件的最佳实践。

import re
def read_filea():
with open("../prod_sum_copy.txt", "r") as text_file:
content = text_file.read()[1:-1]
return content
content = read_filea()
print(content)
def a():
y_val3 = [0]*10000
content_list = content.split(",")
for idx, element in enumerate(content_list):
m = re.search('mpz(([0-9]+))', element)
y_val3[idx]=int(m.group(1))
return y_val3

通过对数据处理部分进行计时,我们可以看到,在这个简单的测试用例中,我们对文件读取(IO)的预测似乎起到了很大的作用。它还为我们提供了一个想法,即仅数据处理部分就应该花费多少时间。让我们看看另一种方法,看看我们是否可以把时间缩短一点。

%timeit read_filea()
21.5 µs ± 185 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

简化的数据处理方法(和单独的Readfile)

在这里,我们将尝试使用一些Python最佳实践或Python工具来减少总体时间,包括:

  • 列表理解
  • 使用re.findall()方法来消除对re.search()函数的一些直接和重复调用,以及对m.group()方法的一些直接或重复调用(注意:findall可能是在后台进行的,我真的不知道我们避免它是否会有好处)。但我发现这种方法的可读性比原来的方法更高

让我们看看代码:

import re
def read_fileb():
with open("../prod_sum_copy.txt", "r") as text_file:
content = text_file.read()[1:-1]
return content
content = read_fileb()
def b():
y_val3 = [int(element) for element in re.findall(r'mpz(([0-9]+))', content)]
return y_val3

该方法的数据处理部分比原始方法中的数据处理步骤快大约10倍。

%timeit b()
2.89 µs ± 210 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)  

有一个聪明的技巧,可以将Python格式打印的数据转换回原始对象。只需执行obj = eval(string),完整示例如下。

您可以将此eval解决方案用于几乎任何python对象,甚至是通过print(python_object)或类似方式打印到文件中的复杂对象。基本上,任何一个有效的python代码都可以通过eval()从字符串转换。

eval()允许完全不使用任何字符串处理/解析函数,不使用正则表达式或其他函数。

请注意,eval()不会检查它运行的字符串,所以如果它来自未知来源,里面的字符串可能会有恶意代码,这些代码可能会对你的电脑造成任何影响,所以只使用受信任的代码字符串执行eval(。

下面的代码使用了带有示例文件内容的text字符串。我使用字符串而不是文件作为示例,这样我的代码就可以完全由StackOverflow访问者运行,而不需要依赖项。在只读打开文件f的情况下,只需将for line in text.split('n'):替换为for line in f:,代码就可以工作了。

在线试用!

from gmpy2 import mpz
text = '''
[mpz(12), mpz(34), mpz(56)]
[mpz(78), mpz(90), mpz(21)]
'''
nums = []
for line in text.split('n'):
if not line.strip():
continue
nums.append(eval(line))
print(nums)

输出:

[[mpz(12), mpz(34), mpz(56)], [mpz(78), mpz(90), mpz(21)]]

相关内容

  • 没有找到相关文章

最新更新