从Python元组中弹出/删除项目

46

我不确定是否表述清楚,但我会尝试。

我在python中有一个元组,如下所示(请参见以下代码)。 在遍历它时,我保持计数器(我们称其为“n”)并“弹出”满足某些条件的项。

现在当我弹出第一个项目时,编号全部都错了,如何更优雅地做到我想要做的事情,同时仅在运行时删除元组的特定条目?

for x in tupleX:
  n=0
  if (condition):
     tupleX.pop(n)
  n=n+1

6
元组是不可变的,并且没有 pop 方法。你确定要讨论的是一个列表吗? - DSM
@DSM在6年前是正确的,但Python 3允许对元组进行切片,因此可以有效地弹出。 - Konchog
10个回答

80
如DSM所述,元组(tuple)是不可变的,但即使对于列表(list),更优雅的解决方案也是使用filter函数:
tupleX = filter(str.isdigit, tupleX)

如果condition不是一个函数,则可以使用推导式:

tupleX = [x for x in tupleX if x > 5]

如果你需要tupleX是一个元组,那么可以使用生成器表达式并将其传递给tuple

tupleX = tuple(x for x in tupleX if condition)

7
对于其他人的参考,生成器表达式在速度方面是最优的。 - Alecg_O
@Alecg_O:你能提供一些支持这个说法的基准测试吗?实际上,在condition是一个(未应用)C函数的情况下(就像我提供的例子一样),filter通常更快;生成器表达式必须执行一些Python字节码,但不需要创建调用帧(也如同我在列表推导式的例子中所示)。 - SingleNegationElimination
根据文档,过滤器和生成器解决方案在功能上是相同的 - 都返回一个由原始条件过滤后的可迭代对象,根据这个定义,两者都是常数时间。然而,假设OP需要一个元组作为输出,区别在于转换回来的过程。证明:从生成器中去掉tuple(),它们都会立即返回,但是对过滤器使用tuple(),它将花费与tupleX大小成比例的更长时间。 - Alecg_O
1
有趣的旁注:我在寻找从元组中“删除”单个项目的最有效方法时遇到了这篇文章。虽然这些都是很好的解决方案,但具有讽刺意味的是,“x = list(x); x.remove(<item>); x = tuple(x)”比它们中的任何一个都要快:P - Alecg_O

18

是的,我们可以做到。 首先将元组转换为列表,然后删除列表中的元素,接着再将其转换回元组。

演示:

my_tuple = (10, 20, 30, 40, 50)

# converting the tuple to the list
my_list = list(my_tuple)
print my_list  # output: [10, 20, 30, 40, 50]

# Here i wanna delete second element "20"
my_list.pop(1) # output: [10, 30, 40, 50]
# As you aware that pop(1) indicates second position

# Here i wanna remove the element "50"
my_list.remove(50) # output: [10, 30, 40]

# again converting the my_list back to my_tuple
my_tuple = tuple(my_list)


print my_tuple # output: (10, 30, 40)

谢谢


2
这是一个糟糕的方法。你会分配一个列表,执行一个操作,然后再分配一个元组。更快、更优雅的方法是创建一个新元组,而不是带有该元素的旧元组。 - Sadderdaze

8
在Python 3中,这不再是一个问题,你真的不想像这样使用列表推导、强制转换、过滤器、函数或lambda表达式。
只需使用:
popped = unpopped[:-1]

记住这是一个不可变对象,如果你想改变它,你需要重新赋值

my_tuple = my_tuple[:-1]

范例

>>> foo= 3,5,2,4,78,2,1
>>> foo
(3, 5, 2, 4, 78, 2, 1)
foo[:-1]
(3, 5, 2, 4, 78, 2)

如果你想要弹出(删除)一个元素,

>>> foo= 3,5,2,4,78,2,1
>>> foo
(3, 5, 2, 4, 78, 2, 1)
>>> foo, bit = foo[:-1], foo[-1]
>>> bit
1
>>> foo
(3, 5, 2, 4, 78, 2)

或者,要使用元组中每个值并从后面开始,请使用以下代码:

foo = 3,5,2,4,78,2,1
for f in reversed(foo):
    print(f)  # 1; 2; 78; ...

或者,使用计数器来检测循环是否完成:

foo = 3,5,2,4,78,2,1
for f, i in enumerate(reversed(foo)):
    print(i, f)  # 0 1; 1 2; 2 78; ...

或者,将其强制转换为列表...

bar = [*foo]
#or 
bar = list(foo)

4

好的,我想出了一个粗糙的方法来做到这一点。

当满足条件时,在 for 循环中将“n”值存储在列表中(我们称之为 delList),然后执行以下操作:

    for ii in sorted(delList, reverse=True):
    tupleX.pop(ii)

欢迎提出其他建议。


3
也许您需要字典?
d = dict( (i,value) for i,value in enumerate(tple))
while d:
    bla bla bla
    del b[x]

1
不,我必须使用元组,因为它是由一个外部函数提供给我的,而我无法编辑这个具有太多依赖关系的旧分层代码。 - Steve Grafton
谢谢,我会尝试这个方法——比我的方法更优雅。 - Steve Grafton

1

有一个简单但实用的解决方案。

正如DSM所说,元组是不可变的,但我们知道列表是可变的。因此,如果你将元组转换为列表,它就会变成可变的。然后你可以通过条件删除项目,然后再将类型改回元组。就这样。

请看下面的代码:

tuplex = list(tuplex)
for x in tuplex:
  if (condition):
     tuplex.pop(tuplex.index(x))
tuplex = tuple(tuplex)
print(tuplex)

例如,以下过程将从给定的元组中删除所有偶数。
tuplex = (1, 2, 3, 4, 5, 6, 7, 8, 9)
tuplex = list(tuplex)
for x in tuplex:
  if (x % 2 == 0):
     tuplex.pop(tuplex.index(x))
tuplex = tuple(tuplex)
print(tuplex)

如果您测试最后一个元组的类型,您会发现它是一个元组。
最后,如果您想像您所做的那样定义一个索引计数器(即n),您应该在循环之前初始化它,而不是在循环中。

1
也许有一种更短的方式:
tup = (0, 1, 2, 3)
new_tup = (*tup[:-2], tup[-1])
print(new_tup) # (0, 1, 3)

0

一个解决方案是将其转换为 set,然后再转换回元组

tupleX = (
    "ZAR",
    "PAL",
    "SEV",
    "ALC",
    "LPA",
    "TFN",)

remove = (
    "LPA",
    "TFN",)

tuple(set(tupleX) - set(remove))

('ZAR','PAL','ALC','SEV')


0

最佳解决方案是将元组应用于列表推导式,但要提取一个项目,可以尝试以下方法:

def pop_tuple(tuple, n):
    return tuple[:n]+tuple[n+1:], tuple[n]

0
假设你有一个以元组为键的字典,例如:labels = {(1,2,0): 'label_1'},你可以按照以下方式修改元组键的元素:
formatted_labels = {(elem[0],elem[1]):labels[elem] for elem in labels}

在这里,我们忽略最后一个元素。


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