Python中使用索引的原地删除函数

11
我注意到Python中没有一种函数可以根据索引从列表中删除项目,以便在链式使用时使用。
例如,我正在寻找像这样的东西:
another_list = list_of_items.remove[item-index]
而不是
del list_of_items[item_index]
由于remove(item_in_list)返回在删除item_in_list后的列表;我想知道为什么没有类似索引的功能。这似乎非常明显和微不足道,应该被包含,感觉有原因跳过它。
对于为什么没有这样的函数的任何想法?
-----编辑------- list_of_items.pop(item_at_index)不适用,因为它不返回不包含要删除特定项的列表,因此无法用于链式。 (根据文档:L.pop([index])-> item - 删除并返回索引处的项)

3
Python中的原地操作通常返回None,而不是修改后的对象。既然标准的Python可变类型没有提供这种方法,你为什么期望能够链式调用呢? - Martijn Pieters
我以为它会像 remove() 一样存在。它返回已更改的对象。 - Jikku Jose
2
不,list.remove() 返回的是 None,而不是已更改的序列。请参见 http://ideone.com/mEH1LL - Martijn Pieters
哦,是的!那是个错误。抱歉。 - Jikku Jose
不知道为什么这个被踩了。这是一个完全有效的问题,并且有一个答案在这里(https://dev59.com/9mLVa4cB1Zd3GeqPuj8X)(答案是:使用列表推导式)。 - LondonRob
4个回答

3

以下是使用Pythonic的方式,通过列表推导式enumerate(请注意,enumerate是从零开始索引的)进行操作:

>>> y = [3,4,5,6]
>>> [x for i, x in enumerate(y) if i != 1] # remove the second element
[3, 5, 6]

这种方法的优点是你可以同时做几件事情:

>>> # remove the first and second elements
>>> [x for i, x in enumerate(y) if i != 0 and i != 1]
[5, 6]
>>> # remove the first element and all instances of 6
>>> [x for i, x in enumerate(y) if i != 0 and x != 6]
[4, 5]

1
OP在标题中提到了“in-place”。列表推导返回一个新列表。 - falsetru
3
正确的标题提到了“原地”。问题询问的是链式调用,而这将允许链式调用。事实上,问题要求的是“原地”的相反操作。x.remove()是原地的,但不允许链式调用。 - LondonRob
"in-place" 方法可以链接使用。请检查我的更新答案。 ;) - falsetru

2
使用 list.pop 方法:
>>> a = [1,2,3,4]
>>> a.pop(2)
3
>>> a
[1, 2, 4]

根据文档:
s.pop([i])
相当于 x = s[i]; del s[i]; 返回 x 更新 为了链式操作,可以使用以下技巧(使用一个包含原始列表的临时序列):
>>> a = [1,2,3,4]
>>> [a.pop(2), a][1] # Remove the 3rd element of a and 'return' a
[1, 2, 4]
>>> a # Notice that a is changed
[1, 2, 4]

1
-1 正如 @BartoszKP 所说,pop 不能用于链接(尽管问题的标题有些令人困惑,但这正是 OP 想要的)。 - LondonRob
@LondonRob,感谢您告诉我原因。我更新了答案,允许链接。 - falsetru
@falsetru 的负分已经被移除了。我会对你的答案进行一些解释,它非常好。(尽管它改变了 a - LondonRob

1
为了获得通过索引删除(即一个新列表,而不是原地删除)单个项目的结果,没有理由使用enumerate或列表推导式或任何其他手动明确的迭代。相反,只需切片列表之前和之后,并将这些部分组合在一起即可。因此:
def skipping(a_list, index):
    return a_list[:index] + a_list[index+1:]

让我们来测试一下:

>>> test = list('example')
>>> skipping(test, 0)
['x', 'a', 'm', 'p', 'l', 'e']
>>> skipping(test, 4)
['e', 'x', 'a', 'm', 'l', 'e']
>>> skipping(test, 6)
['e', 'x', 'a', 'm', 'p', 'l']
>>> skipping(test, 7)
['e', 'x', 'a', 'm', 'p', 'l', 'e']
>>> test
['e', 'x', 'a', 'm', 'p', 'l', 'e']

请注意,它没有抱怨索引超出范围,因为切片通常不会抱怨;如果您想要引发异常,则必须明确检测到此问题。如果我们希望负索引按照Python的常规索引规则工作,我们也必须特殊处理它们,或者至少处理-1(留给读者自行理解原因)。

修复这些问题:

def skipping(a_list, index):
    count = len(a_list)
    if index < 0:
        index += count
    if not 0 <= index < count:
        raise ValueError
    return a_list[:index] + a_list[index+1:]

-1

正如Martijn Pieters在评论中指出的那样,这并没有被实现为:Python中的原地操作通常返回None,而不是修改后的对象。


问题是为什么没有一个不在原地修改的等价物,并且如何提供一个。 - Karl Knechtel
哦,我刚注意到这是你自己的问题!希望它能给你一些思考。例如,我的答案中的“跳过”功能本来可以提供为一个方法,只是没有这样做。我想其中一个原因是很难从名称上清楚地表明输入应该是索引还是要搜索的值。 - Karl Knechtel

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