【问题标题】:How to cleanly loop over two files in parallel in Python如何在Python中并行干净地循环两个文件
【发布时间】:2009-12-02 03:53:53
【问题描述】:

我经常写这样的代码:

lines = open('wordprob.txt','r').readlines()
words = open('StdWord.txt','r').readlines()
i = 0
for line in lines:
    v = [eval(s) for s in line.split()]
    if v[0] > v[1]:
        print words[i].strip(),
    i += 1

是否可以避免使用变量 i 并使程序更短?

谢谢。

【问题讨论】:

    标签: python


    【解决方案1】:

    你可以尝试使用枚举,

    http://docs.python.org/tutorial/datastructures.html#looping-techniques

    lines = open('wordprob.txt','r').readlines()
    words = open('StdWord.txt','r').readlines()
    for i,line in enumerate(lines):
            v = [eval(s) for s in line.split()]
            if v[0] > v[1]:
                    print words[i].strip()
    

    【讨论】:

    • 我不喜欢这个解决方案,因为它会将所有数据读入 RAM。这没关系,只要数据适合 RAM,而且很简单,但我更喜欢适用于任何大小数据集的通用解决方案。
    【解决方案2】:

    您似乎并不关心i 的值是多少。您只是将它用作配对lineswords 的一种方式。因此,我建议你一次读一行,同时读一个单词。然后他们会匹配。

    此外,当您使用.readlines() 时,您会一次将所有输入读入内存。对于大输入,这将很慢。对于这个简单的代码,一次一行就足够了。 open() 返回的文件对象可以作为迭代器,一次返回一行。

    如果可以,您应该避免使用eval()。在一个你知道输入数据是什么的简单练习中,它是相当安全的,但是如果你从外部来源获取数据,使用eval() 可能会让你的计算机受到攻击。请参阅this page 了解更多信息。我将编写我的示例代码,假设您使用eval() 将文本转换为float 值。 float() 也适用于整数字符串值:float('3') 将返回 3.0

    此外,输入行似乎只能有两个值。如果一行有额外的值,您的代码将不会检测到这种情况。我们可以更改代码以显式地从拆分行解包两个值,然后如果有两个以上的值,Python 将引发异常。另外,代码会稍微好读一些。

    所以这是我对这个例子的建议重写:

    lines = open('wordprob.txt','rt')
    words = open('StdWord.txt','rt')
    
    for line in lines:
        word = words.next().strip()  # in Python 3: word = next(words).strip()
        a, b = [float(s) for s in line.split()]
        if a > b:
            print word,  # in Python 3: print(word + ' ', end='')
    

    编辑:这是相同的解决方案,但使用izip()

    import itertools
    lines = open('wordprob.txt','rt')
    words = open('StdWord.txt','rt')
    
    # in Python 3, just use zip() instead of izip()
    for line, word in itertools.izip(lines, words):
        word = word.strip()
        a, b = [float(s) for s in line.split()]
        if a > b:
            print word,  # in Python 3: print(word + ' ', end='')
    

    在 Python 3 中,内置的 zip() 返回一个迭代器,因此您可以直接使用它而不需要 import itertools

    编辑: 无论如何,最好使用with 语句来确保正确关闭文件。在最新版本的 Python 中,您可以有多个 with 语句,我将在我的解决方案中这样做。此外,我们可以像解压列表一样轻松地解压生成器表达式,因此我将设置a, b 的行更改为使用生成器表达式;那应该稍微快一点。而且我们不需要剥离word,除非我们要使用它。将更改放在一起得到:

    from itertools import izip
    
    with open('wordprob.txt','rt') as lines, open('StdWord.txt','rt') as words:
        # in Python 3, just use zip() instead of izip()
        for line, word in izip(lines, words):
            a, b = (float(s) for s in line.split())
            if a > b:
                print word.strip(),  # in Python 3: print(word.strip() + ' ', end='')
    

    【讨论】:

      【解决方案3】:

      一般来说,枚举是一个很好的解决方案。在这种情况下,您可以执行以下操作:

      lines = open('wordprob.txt','r').readlines()
      words = open('StdWord.txt','r').readlines()
      for word, line in zip(words, lines):
          v = [eval(s) for s in line.split()]
          if v[0] > v[1]:
                  print word.strip(),
      

      【讨论】:

      • zip() 是一种将两个事物循环在一起的好方法。但是,在 Python 2.x 中,它将构建一个包含所有值的列表,因此这将占用大量内存。您可以使用itertools.izip() 获得相同的效果,它返回一个迭代器,一次返回一个值。你可以import itertools 然后这样做:for word, line in itertools.izip(open('wordprob.txt'), open('StdWord.txt')):
      • @steveha:或者,您可以通过 from future_builtins import map, filter, zip 选择加入函数的 Py3 版本,然后将 Py2 版本替换为基于 Py3 迭代器的版本。
      【解决方案4】:

      看看enumerate

      >>> for i, season in enumerate(['Spring', 'Summer', 'Fall', 'Winter']):
      ...     print i, season
      0 Spring
      1 Summer
      2 Fall
      3 Winter
      

      【讨论】:

        猜你喜欢
        • 2010-09-25
        • 2011-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-27
        • 2011-08-28
        • 2012-11-28
        相关资源
        最近更新 更多