在枚举列表对象中跳过迭代(Python)

14

我有这段代码

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

正如您所读的,我想让for循环在满足条件时跳过5次迭代。如果条件得以满足,我可以确定"lines"对象中还剩下5个或更多的物体。

"lines"由一组字典数组组成,必须按顺序进行循环遍历。


在这种情况下,我认为while循环会更有效率。 - Tanveer Alam
我需要在我的代码中知道迭代的次数,那么我该如何使用while实现呢? - pidgey
continue 命令将循环移动到下一个可迭代对象。 - kilojoules
@kilojoules,那只会跳过一行。 - Padraic Cunningham
6个回答

15
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

这真的取决于你正在迭代的内容以及你想要做什么。

使用你自己的代码和 iterislice

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)

1
@pidgey 这有什么问题吗?为什么需要将其糖化成某种习语?这很简短,直接,从代码中可以清楚地看出意图。一点问题都没有。 - ely
正如我在问题中提到的那样,这是一个字典列表。 - pidgey
@PadraicCunningham:但是next(lines)在enumerate()上不起作用,对吗? - pidgey
@pidgey,如果你像我对range做的那样调用iter,那它就会返回可迭代对象。你也可以在这个迭代器上调用itertools.islice函数跳过行 https://docs.python.org/2/library/itertools.html#itertools.islice - Padraic Cunningham
@pidgey,没问题,不用谢。如果不知道你想要做什么的全部上下文,就没有一个确定的答案,但这应该会指引你朝着正确的方向前进。 - Padraic Cunningham
显示剩余6条评论

9

这种做法的标准惯用语是创建一个迭代器,然后使用其中的一种消费模式(见这里itertools文档中)。

例如:

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

6

使用枚举索引

类似于被接受的答案...... 但不使用 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 以提高性能。)

1

您可以使用递归的函数式编程风格,首先将 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循环,尽管我倾向于组织我的项目和API,以便使用递归函数方法仍然是高效且易读的。 我还会尝试不要在循环内允许副作用。

0
如Padraic Cunningham所述,您可以使用while循环来实现此操作,也可以使用字典来替换if语句:
iline = 0
skip = {True:5, False:1}

while iline > len(lines):
    line = lines[iline]
    ...
    iline += skip[condition]

0

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

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

我个人更喜欢第一种方法,但第二种方法看起来更符合Python的风格。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接