如何简化此Python代码(从书中分配)?

时间:2019-01-20 15:12:25

标签: python simplify

我正在研究Charles R. Severance撰写的"Python for Everybody"书,对第7章的练习2有疑问。

任务是浏览mbox-short.txt文件,然后“遇到以“ X-DSPAM-Confidence:开始的行:”将线分开以提取该行上的浮点数。这些行,然后从这些行计算垃圾邮件可信度值的总和。到达文件末尾时,请打印出平均垃圾邮件可信度。”

这是我执行此任务的方式:

fname = input('Enter the file name: ') 
try:
    fhand = open(fname) 
except:
    print('File cannot be opened:', fname)
    exit()
count = 0
values = list()
for line in fhand:
    if line.startswith('X-DSPAM-Confidence:'): 
        string = line
        count = count + 1
        colpos = string.find(":")
        portion = string[colpos+1:]
        portion = float(portion)
        values.append(portion)   
print('Average spam confidence:', sum(values)/count)

我知道这段代码行得通,因为我得到的结果与本书中的结果相同,但是,我认为这段代码可以更简单。我之所以这样认为,是因为我在此代码中使用了一个列表(声明它,然后在其中存储值)。但是,“列表”是本书中的下一个主题,在解决此任务时,我对列表一无所知,因此必须使用Google进行搜索。我以这种方式解决了这个任务,因为这是我在R语言(我已经很熟悉)中要做的事情,因此我将创建一个向量,用于存储迭代中的值。

所以我的问题是:这段代码可以简化吗?我可以不使用列表来执行相同的任务吗?如果是,该怎么办?

3 个答案:

答案 0 :(得分:1)

我可以将“值”对象更改为浮动类型。问题中实际上并不需要列表的开销。

config.eager_load = true

然后在循环中使用

values = 0.0

否则,实际上没有简单的方法,因为此问题包含任务,您必须满足所有任务才能解决。

  1. 打开文件
  2. 检查错误
  3. 环行
  4. 查找某些行
  5. 总共说几行
  6. 平均打印

如果您可以用3行代码来完成此操作,但这并不能使在后台进行的操作变得更加简单。它也可能看起来很难看。

答案 1 :(得分:0)

列表理解通常可以替换添加到列表中的for循环:

fname = input('Enter the file name: ') 
try:
    fhand = open(fname) 
except:
    print('File cannot be opened:', fname)
    exit()

values = [float(l[l.find(":")+1:]) for l in fhand if l.startswith('X-DSPAM-Confidence:')]

print('Average spam confidence:', sum(values)/len(values))

内部只是您的代码组合,因此可读性较低。

编辑:不使用列表,可以使用“ reduce”来完成:

from functools import reduce
fname = input('Enter the file name: ') 
try:
    fhand = open(fname) 
except:
    print('File cannot be opened:', fname)
    exit()

sum, count = reduce(lambda acc, l: (acc[0] + float(l[l.find(":")+1:]), acc[1]+1) if l.startswith('X-DSPAM-Confidence:') else acc, fhand, (0,0))

print('Average spam confidence:', sum / count)

Reduce在其他语言中通常称为“折叠”,它基本上允许您使用“累加器”对集合进行迭代。在这里,我使用一个累加器(即(sum, count)的元组)来迭代该集合。对于每个项目,我们将总和相加并增加计数。参见Reduce documentation

所有这些,“简化”并不一定意味着要尽可能少的代码,因此如果您对这些简写表示不满意,我会坚持使用自己的代码。

答案 2 :(得分:0)

您可以在循环之前过滤文件的行,然后可以将其他变量折叠为一个,并使用list-comprehension获得值。这样,您就可以从该列表的长度中获得计数。

interesting_lines = (line.startswith('X-DSPAM-Confidence:') for line in fhand)
values = [float(line[(line.find(":")+1):]) for line in interesting_lines]
count = len(values)
  

我可以不使用列表来执行相同的任务吗?

如果输出需要取平均值,可以,您可以将总和和计数作为自己的变量进行累加,并且不需要列表来针对{p> 1来调用请注意,sum(values)仍在为您提供一个可迭代的集合,并且您正在循环访问文件中的“行列表”。