【问题标题】:Fixing Nested For Loops修复嵌套的 For 循环
【发布时间】:2014-09-21 06:44:31
【问题描述】:

我在让一些嵌套的“for 循环”按照我需要的方式工作时遇到了一些麻烦。我一直在寻找答案,这似乎经常发生,但由于我自己对 Python 还是很陌生,所以解释并没有太大帮助。我有一个单词列表和一个文件,我正在使用它,并且希望列表中的每个单词逐行遍历文件,如果该行包含该单词,则打印该行。目前,当我运行代码时,它只打印出包含列表中第一个单词的行,而不会继续处理列表中的其余单词。

您能否就我如何完成这项工作提供一些建议?

注意:我知道“效率”拼写错误,这是数据源的问题。

编辑:我需要对行进行分组,因此打印所有包含“速度”的行,然后打印所有包含“加速度”的行等。文件中的所有行都包含 SHEETS 中的单词之一。

SHEETS = [' Speed',' Acceleration',' Engine Power',' Instantaneous Fuel Effeciency',
          ' Average Fuel Effeciency',' Instantaneous MPG',' Average MPG',
          ' MAF air flow rate',' Accelerator pedal position E',
          ' Commanded throttle actuator']


with open('userdata.log','r',encoding = 'utf-8') as my_file:
    for label in SHEETS:
        for line in my_file:
            if label in line:
                print (line)

输出:

2014-09-20 14:08:41.165,速度,0,英里/小时

2014-09-20 14:08:43.742,速度,0,英里/小时

2014-09-20 14:08:47.872,速度,0,英里/小时

2014-09-20 14:08:49.490,速度,0,英里/小时

2014-09-20 14:08:51.007,速度,0,英里/小时

2014-09-20 14:08:52.456,速度,0,英里/小时

2014-09-20 14:08:53.888,速度,0,英里/小时

2014-09-20 14:08:55.499,速度,0,英里/小时

2014-09-20 14:08:57.288,速度,0,英里/小时

2014-09-20 14:08:57.838,速度,0,英里/小时

2014-09-20 14:08:58.355,速度,0,英里/小时

2014-09-20 14:08:58.572,速度,0,英里/小时

【问题讨论】:

    标签: python loops for-loop iterator python-3.4


    【解决方案1】:

    发生这种情况是因为该循环第一次运行时:

    for label in SHEETS:
        for line in my_file:
    

    它遍历整个文件,然后停止(它不会“倒带”并从顶部重新开始)。所以它所做的是获取第一个单词并搜索整个文件......然后由于文件已经被搜索过(line 在最后一行),它没有找到你的其他单词。

    在您的情况下,简单的解决方案是切换您的逻辑:对于文件中的每一行,查看它是否包含任何单词。这样您就可以在每一行中搜索所有单词(而不是整个文件中效率较低的一个单词)。

    最终结果是相同的 - 您将打印任何包含您要查找的单词的行。实现非常简单,只需切换循环的顺序即可:

    with open('userdata.log','r',encoding='utf-8') as my_file:
        for line in my_file:
            for label in SHEETS:
                if label in line:
                    print(line)
    

    我需要对行进行分组,因此所有包含“速度”的行 打印,然后打印所有包含“加速”的行等。所有 文件中的行包含 SHEETS 中的单词之一。

    啊,这是另一回事。为此,您需要使用字典,它是 Python 的键/值存储容器。

    字典是您可以存储或分组事物并通过键引用它们的地方。

    在您的情况下,您希望将与单词匹配的所有行组合在一起,因此您的关键是单词,而事物将是行的集合。在字典中,每个键都有一个 list 作为值(列表是许多容器类型之一,另一个是元组)。

    lines_by_word = {}  # This is how you create an empty dictionary
    with open('userdata.log', 'r', encoding='utf-8') as my_file:
       for line in my_file:
          for label in SHEETS:
              if label in line:
                  # Now we have a match - next step is to
                  # collect it. However, if this is the first time
                  # we have encountered this word, we need to add it
                  # to the dictionary
                  if label not in lines_by_word:
                     # By default, dictionary return
                     # their keys in a "in" test (called a membership test)
                     # if the word doesn't exist, we need to create a blank
                     # list for it and add it to the dictionary
                     lines_by_word[label] = []
    
                  lines_by_word[label].append(line) # Simply add the matching line
                                                    # to the list for that word
    
    for word,lines in lines_by_word.iteritems():
        print('There are total of {} lines for {}'.format(word, len(lines))
        for line in line:
            print(line)
    

    【讨论】:

    • 太棒了。我忽略了这一点
    • 我今天早些时候尝试过这个,但我最终只打印了文件中的每一行,我在原始问题中添加了更多关于我试图获得的结果的信息。
    【解决方案2】:

    并非 Python 中的所有内容都支持重复迭代。通常,有两类可迭代对象:迭代器,您只能迭代一次,多用途迭代器,可以迭代任意多次。文件对象属于第一类。

    如果获得预期的特定结果顺序很重要,则可以在循环之后将文件位置重置为开头:

    with open('userdata.log','r',encoding = 'utf-8') as my_file:
        for label in SHEETS:
            for line in my_file:
                if label in line:
                    print (line)
            my_file.seek(0)
    

    您还可以考虑交换循环的顺序并将行收集到每个标签的列表中,然后在最后打印它们。由于 I/O 更少,这可以运行得更快:

    labeled_lines = {label: [] for label in SHEETS}
    with open('userdata.log','r',encoding = 'utf-8') as my_file:
        for line in my_file:
            for label in SHEETS:
                if label in line:
                    labeled_lines[label].append(line)
                    break
            else:
                # else on a loop means "if the loop didn't end with a break."
                raise SomeAppropriateException
    for label in SHEETS:
        for lines in labeled_lines[label]:
            print(line)
    

    最后,您将从文件中读取的行的末尾通常会有换行符。 (唯一可能的例外是文件的最后一行。)由于print 添加了自己的换行符,这将导致每行输出后都有一个空行。您可能需要删除换行符以避免这种情况。

    【讨论】:

    • 谢谢! @user2357112 添加 my_file.seek(0) 效果很好!我也将使用您提供的第二个选项,只需要先把我的头绕过去。谢谢!
    • 在我看来,搜索从来都不是一个好主意 - 以正确的顺序迭代总是一个更好的主意。
    • @TonySuffolk66:确实!使用 seek() 并在文件上执行多次传递应该在别无选择的情况下使用,例如文件太大以至于生成的数据太大而无法放入 RAM。即便如此,我还是倾向于将它写入一堆单独的输出文件。在这种情况下,通过多通道方法创建的打印列表无论如何都会变得笨拙。
    【解决方案3】:

    我想也许你的意思是:

     SHEETS = [' Speed',' Acceleration',' Engine Power',' Instantaneous Fuel Effeciency',
          ' Average Fuel Effeciency',' Instantaneous MPG',' Average MPG',
          ' MAF air flow rate',' Accelerator pedal position E',
          ' Commanded throttle actuator']
    
    
    with open('userdata.log','r',encoding = 'utf-8') as my_file:
         for line in my_file:
             for label in SHEETS:
                if label in line:
                    print (line)
    

    嵌套循环从外到内:对于文件中的每一行,检查该行中是否存在任何标签。

    【讨论】:

    • 我今天早些时候尝试过这个,但我最终只打印了文件中的每一行,我在原始问题中添加了更多关于我想要获得的结果的信息。
    • 如果每一行都包含一个标签,它只会打印每一行。我会回去再检查一次:-)
    • 您的编辑(要求分组)极大地改变了问题陈述,我的两个解决方案都不起作用 - 我的建议是使用 @burhan-Kalid 答案 - 恕我直言,寻求永远不是正确的第一个解决方案.
    猜你喜欢
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多