避免Python代码中的代码重复

时间:2011-05-02 22:45:15

标签: python code-duplication control-flow

考虑以下Python代码段:

af=open("a",'r')
bf=open("b", 'w')

for i, line in enumerate(af):
    if i < K:
        bf.write(line)

现在,假设我想处理KNone的情况, 所以写作继续到文件的末尾。 我正在做

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

这显然不是处理此问题的最佳方法,因为我正在复制代码。 有没有更集成的方式我可以处理这个?自然就是这样 如果if/break不是K,则仅显示None代码, 但这涉及到写一个lappp宏的语法, 哪个Python不能真正做到。为了清楚起见,我并不关心这一点 case(我选择的部分原因是它的简单性),就像学习一般 技术我可能不熟悉。

更新:在阅读了人们发布的答案并做了更多实验之后,这里有更多的评论。

如上所述,我正在寻找可以推广的一般技术,我认为@Paul的答案,即使用takewhile中的iterrools,最适合。作为奖励,它也比我上面列出的天真方法快得多;我不知道为什么。虽然我已经看了好几次,但我并不熟悉itertools。从我的角度来看,这是函数式编程 For Win 的案例! (有趣的是,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合理地建议在KNone的情况下,我只是复制该文件。因为在实践中我无论如何都要循环,这样做并不是一个明确的选择。但是,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

per @Paul的原始例子。但是,尽管代码有效,但我从外部范围获得i这一事实并不完全高兴。有没有更好的方法呢?或者它应该是一个单独的问题。无论如何,感谢所有回答我问题的人。尊敬的@Jeff,他提出了一些很好的建议。

5 个答案:

答案 0 :(得分:5)

for i, line in enumerate(af):  
    if K is None or i < K:
        bf.write(line)
    else:
        break

答案 1 :(得分:2)

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,那么您不希望takewhile永远停止,因此条件函数应始终返回True。但是如果给你一个K的数值,那么一旦元组的第0个元素传递给条件&gt; = K,那么takewhile将会停止。

答案 2 :(得分:1)

如果你必须循环,那怎么样?

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))

为什么在KNone的情况下完全循环?

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)

答案 3 :(得分:1)

无论K是什么,它总是低于无穷大。

if K is None:
    K = float('inf') # infinity

for i, line in enumerate(af):            
    bf.write(line)
    if i==K:
        break

或者,设置K = -1同样有效,尽管它在语义上不太正确。理想情况下,你会在af中设置K = max行,但我认为数据不便宜。

答案 4 :(得分:0)

我认为你处在一种情况下,你必须接受DRY原则和优化之间的权衡。

我首先坚持DRY原则并使用write_until等函数删除重复代码...

def write_until(file_in,file_out,break_on)
    for i,line in enumerate(file_in)

        if break_on(i,line):
            break
        else:
            file_out.write(line)

af=open("a",'r')
bf=open("b", 'w')

if K is None:
    write_until(af,bf,lambda i,line: False)
else:
    write_until(af,bf,lambda i,line: i>K)

然后实际使用代码并查看确实是否需要进行优化。通过删除if False支票,您真实地看到了多少性能改进?如果你真的需要额外的速度提升(我怀疑)那么你将不得不忍受一些代码重复。