【问题标题】:avoiding code duplication in Python code避免 Python 代码中的代码重复
【发布时间】:2011-05-02 22:45:15
【问题描述】:

考虑以下 Python sn-p:

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

这显然不是处理此问题的最佳方法,因为我正在复制代码。 有没有更综合的方法可以处理这个问题?自然的事情是 仅当K 不是None 时才存在if/break 代码, 但这涉及像 Lisp 宏那样动态编写语法, Python 真的做不到。为了清楚起见,我不关心特定的 案例(我选择它的部分原因是为了它的简单性),就像学习一般知识一样 我可能不熟悉的技术。

更新:阅读人们发布的答案并进行更多实验后,这里还有一些 cmets。

如上所述,我一直在寻找可以推广的通用技术,我认为@Paul 的答案,即使用来自iterroolstakewhile 最适合。作为奖励,它也比我上面列出的简单方法快得多;我不确定为什么。我对itertools 不是很熟悉,虽然我看过几次。从我的角度来看,这是一个函数式编程的案例For The 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

根据@Paul 的原始示例。但是,尽管代码有效,但我对从外部范围获得i 这一事实并不完全满意。有没有更好的方法来做到这一点?或者也许它应该是一个单独的问题。无论如何,感谢所有回答我问题的人。向@Jeff 表扬,他提出了一些很好的建议。

【问题讨论】:

  • 只是一个小评论:对于与 None 的比较 is is prefered over using ==.

标签: python code-duplication control-flow


【解决方案1】:
for i, line in enumerate(af):  
    if K is None or i < K:
        bf.write(line)
    else:
        break

【讨论】:

  • 感谢您的回复,但这涉及在运行时检查 K 的两个条件。它没有规模。假设我有更多案例而不是两个案例?
  • 它的扩展性很好。您仍然循环 len(af) 次,并且与 None 相比非常便宜。
【解决方案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 个元素传递给条件 >= K,那么 takewhile 就会停止。

【讨论】:

【解决方案3】:

无论 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 lines,但我认为数据并不便宜。

【讨论】:

    【解决方案4】:

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

    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 的情况下为什么要循环呢?

    从shutil导入副本文件
    
    名称 = 'a' bname = 'b' 如果 K 为无: 复制文件(aname,bname) 别的: af = open(aname, 'r') bf = open(bname, 'w') 对于 i,enumerate(af) 中的行: 如果我

    【讨论】:

    • 嗯,以前使用readline 只会写一行,不是吗?你的意思是read?这会将整个文件转储到内存中。
    • @Faheem:哎呀,我误读了你的问题,并认为在这种情况下只应该写第一行。第二种选择可以简化这一点。
    • @Faheem:在KNone 的情况下,在这种情况下只复制文件而不是在代码中逐行复制会更容易。
    • @Jeff:在我的实际示例中,当 af 进入循环时,它不在文件的开头(一些行已经被读取),所以直接复制是行不通的。不过谢谢你的建议。
    • @Jeff:不需要取消它。这是一个完全有效的答案。
    【解决方案5】:

    我认为您必须在 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 检查后,您会真正看到多少性能改进?如果您真的需要额外的速度提升(我对此表示怀疑),那么您将不得不忍受一些代码重复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多