Python切片能否按索引跳过一个特定元素?

9 投票
3 回答
16994 浏览
提问于 2025-04-17 21:29

假设我在写一个递归函数,我想在一个循环中把一个少了一个元素的list传给这个函数。这里有一个可能的解决方案:

def Foo(input):
    if len(input) == 0: return
    for j in input:
       t = input[:]
       t.remove(j)
       Foo(t)

有没有办法利用切片操作符,直接传递去掉元素j的列表,而不需要显式地复制整个列表并把那个元素删掉呢?

3 个回答

1

这里有一段代码,它和satoru的代码功能一样,但运行得更快,因为它每次只复制一次列表,而不是复制两次:

before = []
after = list_[:]

for x in range(0, len(list_)):
    v = after.pop(0)
    Foo(before + after)
    before.append(v)

(在我的电脑上,对于用list(range(1000))生成的列表,这段代码的运行时间是11毫秒,而satoru的代码是18毫秒)

5

如果你的列表很小,我建议你使用@satoru的回答中的方法。

如果你的列表非常大,并且你想避免频繁创建和删除列表的麻烦,那你可以考虑使用生成器。

import itertools as it
def skip_i(seq, i):
    return it.chain(it.islice(seq, 0, i), it.islice(seq, i+1, None))

这样做可以把跳过第i个元素的工作交给itertools的底层代码来处理,所以这比用纯Python写的方式要快。

如果用纯Python来实现,我建议你可以写一个这样的生成器:

def gen_skip_i(seq, i):
    for j, x in enumerate(seq):
        if i != j:
            yield x

编辑:这是我回答的一个改进版本,感谢下面评论的@Blckknght。

import itertools as it
def skip_i(iterable, i):
    itr = iter(iterable)
    return it.chain(it.islice(itr, 0, i), it.islice(itr, 1, None))

这个改进比我最初的回答要好很多。我最初的回答只适用于可以索引的东西,比如列表,但这个方法对任何可迭代的对象都能正确工作,包括迭代器!它会从可迭代对象中明确创建一个迭代器,然后(以“链”的方式)提取前i个值,之后只跳过一个值,然后提取所有剩下的值。

非常感谢@Blckknght!

7

这个怎么样?

for i in range(len(list_)):
    Foo(list_[:i] + list_[i+1:])

你还是在复制东西,只不过在复制的时候忽略了索引为 i 的那个元素。

顺便说一下,你可以通过在内置名称后面加下划线来避免覆盖它们,比如 list

撰写回答