【问题标题】:Skip iterations in enumerated list object (python)跳过枚举列表对象中的迭代(python)
【发布时间】:2015-03-24 04:10:07
【问题描述】:

我有密码

for iline, line in enumerate(lines):
    ...
    if <condition>:
        <skip 5 iterations>

如您所见,我希望在满足条件时让 for 循环跳过 5 次迭代。我可以肯定,如果满足条件,“lines”对象中还剩下5个或更多对象。

存在字典数组的行,必须按顺序循环

【问题讨论】:

  • while 在这种情况下我认为循环会更有效。
  • 我需要其余代码中的迭代次数,那么我将如何使用一段时间呢?
  • continue 命令将循环移动到下一个可迭代对象
  • @kilojoules,只会跳过一行

标签: python loops skip


【解决方案1】:
iline = 0
while iline < len(lines):
    line = lines[iline]
    if <condition>:
        place_where_skip_happened = iline
        iline += 5
    iline += 1

如果您正在迭代文件对象,您可以使用 next 跳过行或将行设为迭代器:

lines = iter(range(20))

for l in lines:
    if l == 10:
        [next(lines) for _ in range(5)]
    print(l)
0
1
2
3
4
5
6
7
8
9
10
16
17
18
19

这实际上取决于您正在迭代什么以及您想要做什么。

在iter 和islice 中使用您自己的代码:

from itertools import islice


it = iter(enumerate(lines))

for iline, line in it:
    if <condition>:
        place_where_skip_happened = iline
        next(islice(it,5 ,5), None)
    print(line)

【讨论】:

  • 这真的是最美的代码吗?必须在循环末尾添加 iline+=1 吗?
  • @pidgey 那有什么问题?为什么它需要被糖化成一些成语?这很简短,直截了当,从代码中可以清楚地看出意图是什么。一点问题都没有。
  • 正如我提出的问题,字典列表
  • @PadraicCunningham:但是 next(lines) 不会在 enumerate() 上工作,还是会这样?
  • @pidgey,如果你像我对 range 那样调用 iter ,它会的。您也可以在迭代器上调用 itertools.islice 以跳过行 docs.python.org/2/library/itertools.html#itertools.islice
【解决方案2】:

执行此操作的标准习惯用法是创建一个迭代器,然后使用其中一种消费者模式(请参阅 itertools 文档中的 here。)

例如:

from itertools import islice

lines = list("abcdefghij")

lit = iter(enumerate(lines))
for iline, line in lit:
    print(iline, line)
    if line == "c":
        # skip 3
        next(islice(lit, 3,3), None)

生产

0 a
1 b
2 c
6 g
7 h
8 i
9 j

【讨论】:

    【解决方案3】:

    使用枚举索引

    类似于接受的答案...除了不使用itertools(恕我直言islice不会提高可读性),加上enumerate()已经返回一个迭代器,所以你根本不需要iter():

    lines = [{str(x): x} for x in range(20)]  # dummy data
    
    it = enumerate(lines)
    for i, line in it:
        print(line)
    
        if i == 10:  # condition using enumeration index
            [next(it, None) for _ in range(5)]  # skip 5
    

    为了便于阅读,可以选择扩展最后一行:

            for _ in range(5):  # skip 5
                next(it, None)
    

    next() 中的 None 参数可避免在没有足够的项目可跳过时出现异常。 (对于原始问题,可以省略,因为 OP 写道:“我可以确定,如果满足条件,lines 对象中还剩下 5 个或更多对象。”)

    不使用枚举索引

    如果跳过条件不是基于枚举索引,只需将列表视为 FIFO 队列并使用 pop() 从中消费:

    lines = [{str(x): x} for x in range(20)]  # dummy data
    
    while lines:
        line = lines.pop(0)  # get first item
        print(line)
    
        if <condition>:  # some other kind of condition
            [lines.pop(0) for _ in range(5)]  # skip 5
    

    和以前一样,可以选择扩展最后一行以提高可读性:

            for _ in range(5):  # skip 5
                lines.pop(0)
    

    (对于大型列表,请使用collections.deque 来提高性能。)

    【讨论】:

      【解决方案4】:

      您可以使用带有递归的函数式编程风格,首先将for 循环的必要部分放入一个函数中:

      def my_function(iline, line, rest_of_lines, **other_args):
          do_some_side_effects(iline, line, **other_args)
      
          if rest_of_lines == []:
              return <some base case>
      
          increment = 5 if <condition> else 1
          return my_function(iline+increment, 
                             rest_of_lines[increment-1], 
                             rest_of_lines[increment:],
                             **other_args)
      

      如果它不需要返回任何内容,您可以将这些代码行调整为函数调用,返回结果将为None。

      然后是你真正称呼它的某个地方:

      other_args = get_other_args(...)
      
      my_function(0, lines[0], lines[1:], **other_args)
      

      如果您需要该函数为每个索引返回不同的内容,那么我建议稍微修改一下以考虑您想要的输出数据结构。在这种情况下,您可能希望将 do_some_side_effects 的内部结果传递回递归函数调用,以便它可以构建结果。

      def my_function(iline, line, rest_of_lines, output, **other_args):
          some_value = do_some_side_effects(iline, line, **other_args)
      
          new_output = put_value_in_output(some_value, output)
          # could be as simple as appending to a list/inserting to a dict
          # or as complicated as you want.
      
          if rest_of_lines == []:
              return new_output
      
          increment = 5 if <condition> else 1
          return my_function(iline+increment, 
                             rest_of_lines[increment-1], 
                             rest_of_lines[increment:],
                             new_output,
                             **other_args)
      

      然后调用

      other_args = get_other_args(...)
      
      empty_output = get_initial_data_structure(...)
      
      full_output = my_function(0, lines[0], lines[1:], empty_output, **other_args)
      

      请注意,在 Python 中,由于大多数基本数据结构的实现方式,这种编程风格不会提高您的效率,在其他面向对象代码的上下文中,它甚至可能是使事情复杂化的糟糕风格超越简单的while 解决方案。

      我的建议:使用 while 循环,尽管我倾向于构建我的项目和 API,以便使用递归函数方法仍然高效且可读。我也会尽量避免在循环内产生副作用。

      【讨论】:

        【解决方案5】:

        正如 Padraic Cunningham 所说,您可以使用 while 循环来执行此操作,也可以使用字典来替换 if 语句:

        iline = 0
        skip = {True:5, False:1}
        
        while iline > len(lines):
            line = lines[iline]
            ...
            iline += skip[condition]
        

        【讨论】:

          【解决方案6】:

          使用外部标志并在满足条件时设置它并在循环开始时检查它:

          ignore = 0
          for iline, line in enumerate(lines):
              if ignore > 0:
                  ignore -= 1
                  continue
          
              print(iline, line)
          
              if iline == 5:
                  ignore = 5
          

          或者从枚举中显式提取5个元素:

          enum_lines = enumerate(lines)
          for iline, line in enum_lines:
              print(iline, line)
          
              if iline == 5:
                  for _, _ in zip(range(5), enum_lines):
                      pass
          

          我个人更喜欢第一种方法,但第二种看起来更像 Pythonic。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-09-07
            • 2021-12-18
            • 1970-01-01
            • 1970-01-01
            • 2022-12-05
            • 2012-12-14
            • 2017-12-01
            相关资源
            最近更新 更多