【问题标题】:avoiding code duplication in Python code redux避免 Python 代码 redux 中的代码重复
【发布时间】:2011-05-03 19:37:50
【问题描述】:

这是对先前问题的后续处理。我得到了一些很好的建议,所以我想我会再试试运气。

from itertools import takewhile

if K is None:
    illuminacond = lambda x: x.split(',')[0] != '[Controls]'
else:
    illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K

af=open('a')
bf=open('b', 'w')
cf=open('c', 'w')

i = 0
if K is None:
    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
            bf.write(out.strip('"')+'\n')
            cf.write(line)
else:
    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
            bf.write(out.strip('"')+'\n')

是否可以压缩此代码?如果我在这样的两个循环中有一些共同点, 一种明显的可能性是只考虑通用代码,但在这里,eww。 烦人的是这里唯一的区别是写信给c

代码简要总结:如果K 不是None,则遍历aK 行并写入bc。否则,遍历所有a,然后写信给b

【问题讨论】:

    标签: python code-duplication control-flow


    【解决方案1】:

    为什么不只使用一个循环,而是在循环中包含条件inside?另外,我认为您可以摆脱该 lambda 中的冗余。

    from itertools import takewhile
    
    k_is_none = K is None
    
    def illuminacond(x):
        global i
        global K
        result = x.split(',')[0] != '[Controls]'
        if not k_is_none:
            result = result and i < K
        return result
    
    af=open('a')
    bf=open('b', 'w')
    cf=open('c', 'w')
    
    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
            bf.write(out.strip('"')+'\n')
            if k_is_none:
                cf.write(line)
    

    【讨论】:

    • 条件K is None 至少需要检查K 次。否则,不,没有错。诚然,在这种情况下,检查不是很耗时,但假设它是?
    • 像这样缓存它:my_cond = K is None
    • 现在您不是对K 进行一次,而是两次运行时检查。 :-)
    • @faheem-mitha 当@brian-odell 输入他的回复时,我正在编辑我建议的代码。如果您担心这种比较,请执行一次并存储结果。 k_is_none = K is None
    【解决方案2】:

    一次检查,一次循环,无类,心理优化。

    from itertools import takewhile
    
    if K is None:
        illuminacond = lambda x: x.split(',')[0] != '[Controls]'
        def action(cf, line): cf.write(line)
    else:
        illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
        def action(cf, line): pass
    
    af=open('a')
    bf=open('b', 'w')
    cf=open('c', 'w')
    
    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
            bf.write(out.strip('"')+'\n')
            action(cf, line)
    

    【讨论】:

    • 嘿,不错!这里的函数调用成本是多少?
    • 另外,对 psyco 参考资料很感兴趣。虽然对 psyco 几乎一无所知。 Python 代码可心理优化意味着什么?
    • 函数调用成本很大程度上取决于 python 版本/arch。 Psyco 是一个 python 优化器(不幸的是,python > 2.6 目前不支持)。我已经使用this script 进行了一些测量,纯 CPython 函数调用在我的系统上约为 170-190ns/call,CPython+Psyco 提供 ~9-35ns/call。
    • 谢谢,这是非常有趣和有用的信息。我在 Debian 上运行 Python 2.6,所以我可以看到使用 Psyco 有多大的不同。
    【解决方案3】:

    为什么不只是:

    from itertools import takewhile
    
    illuminacond = lambda x: x.split(',')[0] != '[Controls]' and (K is None or i<K) #i'm not so sure about this part, confused me a little :).
    
    af=open('a')
    bf=open('b', 'w')
    cf=open('c', 'w')
    
    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
            bf.write(out.strip('"')+'\n')
            if K is None:
                cf.write(line)
    

    【讨论】:

      【解决方案4】:

      这个(基于二等的版本)怎么样?

      from itertools import takewhile
      
      class Foo:
          def __init__(self, K = None):
              self.bf=open('b', 'w')
              self.cf=open('c', 'w')
              self.count = 0
              self.K = K
      
          def Go(self):
              for self.line in takewhile(self.Lamda(), open('a')):
                  self.SplitLine()
                  if self.IsValidPid():
                      self.WriteLineToFiles()
      
          def SplitLine(self):
              self.lineSplit=self.line.split(',')
      
          def Lamda(self):
              if self.K is None:
                  return lambda x: x.split(',')[0] != '[Controls]'
              else:
                  return lambda x: x.split(',')[0] != '[Controls]' and self.count < self.K
      
          def IsValidPid(self):
              pid=self.lineSplit[1][0:3]
              return pid!='cnv' and pid!='hCV' and pid!='cnv'
      
          def WriteLineToFiles(self):
              self.count += 1
              self.bf.write(self.ParseLine())
              if self.K is None:
                  self.cf.write(self.line)
      
          def ParseLine(self):
              return (self.lineSplit[1] + ',' + self.lineSplit[2] + ',' + 
                      self.lineSplit[3][1] + self.lineSplit[3][3] + ',' +
                      self.lineSplit[15] + ',' + self.lineSplit[9] + ',' + 
                      self.lineSplit[10]).strip('"')+'\n'
      
      Foo().Go()
      

      原版:

      from itertools import takewhile
      
      if K is None:
          illuminacond = lambda x: x.split(',')[0] != '[Controls]'
      else:
          illuminacond = lambda x: x.split(',')[0] != '[Controls]' and i < K
      
      def Parse(line):
          return (line[1] + ',' + line[2] + ',' + line[3][1] + line[3][3] + ',' +
                  line[15] + ',' + line[9] + ',' + line[10]).strip('"')+'\n'
      
      def IsValidPid(line_split):
          pid=line_split[1][0:3]
          return pid!='cnv' and pid!='hCV' and pid!='cnv'
      
      bf=open('b', 'w')
      cf=open('c', 'w')
      
      def WriteLineToFiles(line, line_split):
          bf.write(Parse(line_split))
          if K is None:
              cf.write(line)
      
      i = 0
      
      for line in takewhile(illuminacond, open('a')):
          line_split=line.split(',')
          if IsValidPid(line_split):
              WriteLineToFiles(line, line_split)
              i += 1
      

      【讨论】:

      • 对,这就是我在上面的问题中所说的“排除通用代码”的意思。这当然是一种方法。
      • 我通常从一个抽象层开始,将具有共同兴趣的代码块组合在一起,分解成方法并给它们起好名字。不需要 cmets,并且代码实际上是自我记录的。它本质上是非常迭代和增量的,它应该是这样的:)
      • 添加了第二个基于类的版本,它对事物进行了更多的封装并实现了更好的抽象。通过查看 Go(),您会立即了解代码的作用。
      • 努力+1,但除非绝对必要,否则我不是类方法方法的忠实粉丝。
      • @Faheem:你可以选择其中任何一个,我不在乎:D 必要性可以在多个层面上进行讨论。我个人认为代码越干净,维护和调试的需求就越少。从长远来看,你会受益。如果有人可以继续重构它会很甜蜜:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多