如何在Python循环中仅对第一个项执行某些操作?

11

我想对列表中的第一项进行不同的操作。最符合Python风格的方法是什么?

for item in list:
    # only if its the first item, do something

    # otherwise do something else

使用一个标志,在处理第一项后更改它。 - thegrinner
在提问之前先搜索重复问题。 - martineau
5个回答

21

以下是根据 Pythonic 程度降序排列的一些选择:

for index, item in enumerate(lst): # note: don't use list
    if not index: # or if index == 0:
        # first item
    else:
        # other items

或者:

first = True
for item in lst:
    if first:
        first = False
        # first item 
    else:
        # other items 

或者:

for index in range(len(lst)):
    item = lst[i]
    if not index:
        # first item
    else:
        # other items

4
使用enumerate相比其他选项的一个优点是,无论被迭代的对象是否支持索引,都不会产生影响。 - DSM
3
我喜欢使用enumerate,但不确定是否应该使用if not indexif index==0更加明确。 - Adam Smith
3
@adsmith:PEP 8 的规定与此不同。标准库中也有确切的 if not index: 示例。 - abarnert
1
所有变量都需要在循环体中使用if语句。非常低效。 - volcano
1
@volcano:我不知道你所说的“数千个周期”是什么意思。如果你指的是循环遍历数千个值,那么你需要调用几千次next函数,每次调用比每个if语句花费的时间长几个数量级,因此比例没有改变。 - abarnert
显示剩余2条评论

10

您可以使用iter()在列表上创建迭代器,然后调用next()以获取第一个值,然后循环遍历其余部分。我发现这是一种非常优雅的处理首行为标题,其余都是数据的文件的方式,即:

list_iterator = iter(lst)

# consume the first item
first_item = next(list_iterator)

# now loop on the tail
for item in list_iterator:
    print(item)

1
解释有点偏差 - 你不是将列表转换为迭代器,而是在列表上创建迭代器。因其高效和优雅而获得了赞同。 - volcano
好的观点,我已经进行了编辑以澄清。另外,我喜欢的是调用iter(iter(list))仍然返回内部的<list_iterator object>,因此您无需知道lst是一个列表对象还是一个list_iterator (或其他可迭代对象)。 - David Waterworth
1
我喜欢它,但在所有情况下可能不是最佳解决方案,例如如果循环包含一些语句,这些语句应该对第一个项目以及其他项目执行。在这种情况下,@jonrsharpe提供的答案可能确实是最好的选择。 - PhiM

6
do_something_with_first_item(lst[0])
for item in lst[1:]:
    do_something_else(item)

或者:

is_first_item = True
for item in lst:
    if is_first_item:
        do_something_with_first_item(item)
        is_first_item = False
    else:
        do_something_else(item)

不要使用 list 作为变量名,因为这会掩盖内置函数 list()

在 jonrsharpe 的答案中,基于 enumerate 的解决方案更优。您应该使用那个方案。


2

使用一个标志,在处理第一项后进行更改。例如:

first = True
for item in my_list:
    if first:
        # Processing unique to the first item
        first = False
    else:
        # Processing unique to other items
    # Shared processing

您也可以只处理第一项:
first = my_list.pop(0)
# Process first
for item in my_list:
    # Process item

# Add the first item back to the list at the beginning
my_list.insert(0, first)

+!适用于.pop()。老实说,这是最好、最干净的解决方案。 - mklauber
并非所有可迭代容器都具有pop()方法,例如元组,因此这不是一种通用解决方案。 - martineau

2

jonrsharpe的第一个版本使用enumerate非常简洁明了,适用于所有可迭代对象:

for index, item in enumerate(lst):
    if not index:
        do_something_with_first_item(item)
    else:
        do_something_else(item)

senshin的第一种解决方案是使用lst[0]lst[1:],非常简单,但只适用于序列:

do_something_with_first_item(lst[0])
for item in lst[1:]:
    do_something_else(item)

通过直接使用迭代器,您可以兼顾两者:

it = iter(lst)
do_something_with_first_item(next(it))
for item in it:
    do_something_else(item)

但是,尽管它融合了两种最好的方式,实际上并不像看起来那么简单。只有当您知道您拥有一个迭代器(这样您就可以跳过第一行),或者您需要使用itertools和genexpr等类似的东西时才值得这样做(所以您已经打算写第一行)。在其他情况下是否值得这样做更多的是一个风格问题。


我认为你不需要知道你是否有一个迭代器来使用最后一种方法。我进行了快速检查,调用iter()在迭代器上似乎会返回原始迭代器 - 也就是说,iter(iter([1,2,3]))和iter([1,2,3])都会返回一个列表迭代器对象,该对象迭代原始列表。因此,我会在每种情况下都使用此方法 - 第一种方法很简洁,但需要在循环的每次迭代中测试索引,第二种方法则需要一个可索引的对象。 - David Waterworth
@user2981639:是的,如果it是一个迭代器,iter(it)保证返回it——事实上,这就是迭代器的定义的一部分。关键在于,如果你知道它是一个迭代器,你可以跳过第一行,使这成为最简单的解决方案;如果你有一个序列,或者不知道你有什么类型的可迭代对象,你不能跳过第一行,所以它可能不是最简单的。 - abarnert

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