在Python代码中避免代码重复
考虑以下这段Python代码:
af=open("a",'r')
bf=open("b", 'w')
for i, line in enumerate(af):
if i < K:
bf.write(line)
现在,假设我想处理一个情况,就是当 K 是 None 的时候,写入操作可以继续到文件的末尾。现在我正在这样做:
if K is None:
for i, line in enumerate(af):
bf.write(line)
else:
for i, line in enumerate(af):
bf.write(line)
if i==K:
break
显然,这不是处理这个问题的最佳方法,因为我在重复代码。有没有更好的方法来处理这个问题呢?理想的情况是,只有在 K 不是 None 的时候,才有 if/break 这段代码,但这就涉及到像Lisp宏那样动态写语法,而Python其实做不到这一点。为了明确,我并不太关心这个特定的情况(我选择这个例子部分是因为它简单),我更想了解一些我可能不熟悉的通用技巧。
更新:在阅读了大家的回答并进行了一些实验后,我有了一些新的想法。
如上所述,我在寻找可以推广的通用技巧,我认为 @Paul 的回答,使用 takewhile 来自 itertools,是最合适的。作为额外的好处,它的速度也比我上面提到的简单方法快很多;我不太清楚为什么。我对 itertools 不是很熟悉,虽然我看过几次。从我的角度来看,这就是函数式编程的胜利!(有趣的是,itertools 的作者曾经询问过是否应该去掉 takewhile。可以查看这个讨论 http://mail.python.org/pipermail/python-list/2007-December/522529.html。)我上面简化了我的情况,实际上情况要复杂一些——我在循环中写入两个不同的文件。所以代码看起来更像是:
for i, line in enumerate(af):
if i < K:
bf.write(line)
cf.write(line.split(',')[0].strip('"')+'\n')
根据我发布的例子,@Jeff 合理地建议,当 K 是 None 的时候,我可以直接复制文件。因为实际上我无论如何都在循环,所以这样做并不是一个明确的选择。不过,takewhile 在这个情况下也能轻松推广。我还有一个没有提到的用例,也能在那儿使用 takewhile,这让我觉得很不错。第二个例子看起来是(逐字)
i=0
for line in takewhile(illuminacond, af):
line_split=line.split(',')
pid=line_split[1][0:3]
out = line_split[1] + ',' + line_split[2] + ',' + line_split[3][1] + line_split[3][3] + ',' \
+ line_split[15] + ',' + line_split[9] + ',' + line_split[10]
if pid!='cnv' and pid!='hCV' and pid!='cnv':
i = i+1
of.write(out.strip('"')+'\n')
tf.write(line)
在这里我能够使用这个条件
if K is None:
illuminacond = lambda x: x.split(',')[0] != '[Controls]'
else:
illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
根据 @Paul 的原始例子。不过,我对从外部作用域获取 i 这件事并不是很满意,尽管代码是有效的。有没有更好的方法来做到这一点?或者这可能应该是一个单独的问题。无论如何,感谢所有回答我问题的人。特别感谢 @Jeff,他提出了一些很好的建议。
相关问题:
5 个回答
from shutil import copyfile
aname = 'a' bname = 'b' if K is None: copyfile(aname, bname) else: af = open(aname, 'r') bf = open(bname, 'w') for i, line in enumerate(af): if i < K: bf.write(line)
如果你一定要使用循环,那这样做怎么样?
from sys import maxint
limit = K or maxint
for i, line in enumerate(af):
if i >= limit: break
bf.write(line)
或者这样呢?
from itertools import islice
from sys import maxint
bf.writelines(islice(af, K or maxint))
如果K是None,那为什么还要循环呢?
for i, line in enumerate(af):
if K is None or i < K:
bf.write(line)
else:
break
当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。
itertools.takewhile 这个功能会根据你设定的条件来处理数据,一旦条件不满足,它就会立刻停止。
from itertools import takewhile
if K is None:
condition = lambda x: True
else:
condition = lambda x: x[0] < K
for i,line in takewhile(condition, enumerate(af)):
bf.write(line)
如果 K 是 None,说明你希望这个功能一直运行下去,所以条件函数应该总是返回 True。可是如果你给了一个数字 K,当传给条件的元组的第一个元素大于等于 K 时,takewhile 就会停止。