lfa*_*one 917 python iteration
我正在迭代Python中的元组列表,并且如果它们符合某些条件,我会尝试删除它们.
for tup in somelist:
if determine(tup):
code_to_remove_tup
Run Code Online (Sandbox Code Playgroud)
我应该用什么代替code_to_remove_tup?我无法弄清楚如何以这种方式删除项目.
Dav*_*ick 786
您可以使用列表推导来创建仅包含您不想删除的元素的新列表:
somelist = [x for x in somelist if not determine(x)]
Run Code Online (Sandbox Code Playgroud)
或者,通过分配切片somelist[:],您可以改变现有列表以仅包含所需的项目:
somelist[:] = [x for x in somelist if not determine(x)]
Run Code Online (Sandbox Code Playgroud)
如果有其他参考somelist需要反映更改,则此方法可能很有用.
你也可以使用而不是理解itertools.在Python 2中:
from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)
Run Code Online (Sandbox Code Playgroud)
或者在Python 3中:
from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
Run Code Online (Sandbox Code Playgroud)
Ale*_*lli 565
建议列表推导的答案几乎是正确的 - 除了它们构建一个全新的列表然后给它与旧列表相同的名称,它们不会修改旧的列表.这与你通过选择性删除所做的不同,就像在@ Lennart的建议中一样 - 它更快,但是如果你的列表是通过多个引用访问的,那么你只是重新安装其中一个引用而不是改变列表对象本身可能导致微妙的,灾难性的错误.
幸运的是,获得列表推导的速度和就地更改所需的语义非常容易 - 只需代码:
somelist[:] = [tup for tup in somelist if determine(tup)]
Run Code Online (Sandbox Code Playgroud)
注意与其他答案的细微差别:这个没有分配到一个名字 - 它分配给恰好是整个列表的列表切片,从而替换同一个Python列表对象中的列表内容 ,而不是仅仅重新设置一个引用(从之前的列表对象到新列表对象)就像其他答案一样.
Len*_*bro 276
您需要获取列表的副本并首先迭代它,否则迭代将失败,结果可能是意外结果.
例如(取决于列表的类型):
for tup in somelist[:]:
etc....
Run Code Online (Sandbox Code Playgroud)
一个例子:
>>> somelist = range(10)
>>> for x in somelist:
... somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]
>>> somelist = range(10)
>>> for x in somelist[:]:
... somelist.remove(x)
>>> somelist
[]
Run Code Online (Sandbox Code Playgroud)
Joh*_*hin 116
for i in range(len(somelist) - 1, -1, -1):
if some_condition(somelist, i):
del somelist[i]
Run Code Online (Sandbox Code Playgroud)
你需要倒退,否则就像锯掉你所坐的树枝一样:-)
Python的2个用户:更换range通过xrange避免产生硬编码列表
Eli*_*ght 48
这样一个例子的最佳方法是列表理解
somelist = [tup for tup in somelist if determine(tup)]
Run Code Online (Sandbox Code Playgroud)
如果您正在做一些比调用determine函数更复杂的事情,我更喜欢构建一个新列表,并在我去的时候简单地附加它.例如
newlist = []
for tup in somelist:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
somelist = newlist
Run Code Online (Sandbox Code Playgroud)
使用复制列表remove可能会使您的代码看起来更清晰,如下面的答案之一所述.绝对不应该为非常大的列表执行此操作,因为这涉及首先复制整个列表,并且还对O(n) remove要删除的每个元素执行操作,使其成为O(n^2)算法.
for tup in somelist[:]:
# lots of code here, possibly setting things up for calling determine
if determine(tup):
newlist.append(tup)
Run Code Online (Sandbox Code Playgroud)
Cir*_*四事件 48
在官方的Python 2教程4.2."for Statements"说:
如果您需要修改在循环内迭代的序列(例如复制所选项目),建议您先复制一份.迭代序列不会隐式地复制.切片表示法使这特别方便:
Run Code Online (Sandbox Code Playgroud)>>> words = ['cat', 'window', 'defenestrate'] >>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate']
这是建议的:https://stackoverflow.com/a/1207427/895245
在Python 2里的文档7.3."for声明"给出了同样的建议:
注意:当循环修改序列时有一个微妙的变化(这只能发生在可变序列,即列表中).内部计数器用于跟踪下一个使用的项目,并在每次迭代时递增.当该计数器达到序列的长度时,循环终止.这意味着如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取已经处理的当前项目的索引).同样,如果套件在当前项目之前的序列中插入项目,则下次循环时将再次处理当前项目.这可能导致令人讨厌的错误,可以通过使用整个序列的切片进行临时复制来避免,例如,
Run Code Online (Sandbox Code Playgroud)for x in a[:]: if x < 0: a.remove(x)
Python能做得更好吗?
似乎可以改进这个特定的Python API.例如,将它与Java对应的ListIterator进行比较,这清楚地表明除了迭代器本身之外你不能修改被迭代的列表,并且无需复制列表就可以提供有效的方法.来吧,Python!
Cid*_*ide 37
对于那些喜欢函数式编程的人:
somelist[:] = filter(lambda tup: not determine(tup), somelist)
Run Code Online (Sandbox Code Playgroud)
要么
from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
Run Code Online (Sandbox Code Playgroud)
Mic*_*ael 12
我需要用一个巨大的列表来执行此操作,并且复制列表似乎很昂贵,特别是因为在我的情况下,删除的数量与剩余的项目相比很少.我采用了这种低级方法.
array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
if someTest(array[i]):
del array[i]
arraySize -= 1
else:
i += 1
Run Code Online (Sandbox Code Playgroud)
我不知道的是,将一些删除与复制大型列表相比有多高效.如果您有任何见解,请评论.
ntk*_*tk4 10
如果当前列表项符合所需条件,也可以智能地创建新列表.
所以:
for item in originalList:
if (item != badValue):
newList.append(item)
Run Code Online (Sandbox Code Playgroud)
并避免必须使用新列表名称重新编码整个项目:
originalList[:] = newList
Run Code Online (Sandbox Code Playgroud)
请注意,来自Python文档:
copy.copy(x)返回x的浅表副本.
copy.deepcopy(x)返回x的深层副本.
小智 9
这个答案最初是为了回答一个已被标记为重复的问题而编写的: 从python上的列表中删除坐标
您的代码中存在两个问题:
1)当使用remove()时,您尝试删除整数,而您需要删除元组.
2)for循环将跳过列表中的项目.
让我们来看看执行代码时会发生什么:
>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
... if a < 0 or b < 0:
... L1.remove(a,b)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)
Run Code Online (Sandbox Code Playgroud)
第一个问题是你将'a'和'b'都传递给remove(),但remove()只接受一个参数.那么我们怎样才能让remove()与你的列表一起正常工作呢?我们需要弄清楚列表中每个元素是什么.在这种情况下,每个都是一个元组.为了看到这一点,让我们访问列表中的一个元素(索引从0开始):
>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>
Run Code Online (Sandbox Code Playgroud)
啊哈!L1的每个元素实际上都是一个元组.这就是我们需要传递给remove()的东西.python中的元组非常简单,它们只是通过括在括号中的值来制作."a,b"不是元组,但"(a,b)"是元组.所以我们修改你的代码并再次运行它:
# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))
Run Code Online (Sandbox Code Playgroud)
此代码运行时没有任何错误,但让我们看一下它输出的列表:
L1 is now: [(1, 2), (5, 6), (1, -2)]
Run Code Online (Sandbox Code Playgroud)
为什么(1,-2)仍在您的列表中?事实证明修改列表,而使用循环迭代它是一个非常糟糕的想法,没有特别小心.(1,-2)保留在列表中的原因是列表中每个项目的位置在for循环的迭代之间发生了变化.让我们来看看如果我们将上面的代码提供给更长的列表会发生什么:
L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
Run Code Online (Sandbox Code Playgroud)
正如您可以从该结果推断的那样,每次条件语句的计算结果为true并且删除了列表项时,循环的下一次迭代将跳过对列表中下一项的评估,因为它的值现在位于不同的索引处.
最直观的解决方案是复制列表,然后遍历原始列表并仅修改副本.您可以尝试这样做:
L2 = L1
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)
Run Code Online (Sandbox Code Playgroud)
但是,输出将与之前相同:
'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
Run Code Online (Sandbox Code Playgroud)
这是因为当我们创建L2时,python实际上并没有创建新对象.相反,它仅将L2引用到与L1相同的对象.我们可以用'is'来验证它,这与仅仅是"equals"(==)不同.
>>> L2=L1
>>> L1 is L2
True
Run Code Online (Sandbox Code Playgroud)
我们可以使用copy.copy()创建一个真正的副本.一切都按预期工作:
import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
Run Code Online (Sandbox Code Playgroud)
最后,有一个更清洁的解决方案,而不是制作一个全新的L1副本.reverse()函数:
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
if a < 0 or b < 0 :
L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
Run Code Online (Sandbox Code Playgroud)
不幸的是,我无法充分描述revers()的工作原理.当列表传递给它时,它返回一个'listreverseiterator'对象.出于实际目的,您可以将其视为创建其参数的反向副本.这是我推荐的解决方案.
其他答案是正确的,从您正在迭代的列表中删除通常是一个坏主意。反向迭代避免了一些陷阱,但遵循执行此操作的代码要困难得多,因此通常最好使用列表理解或filter.
然而,在一种情况下,从正在迭代的序列中删除元素是安全的:如果您在迭代时仅删除一项。这可以使用 areturn或 a来确保break。例如:
for i, item in enumerate(lst):
if item % 4 == 0:
foo(item)
del lst[i]
break
Run Code Online (Sandbox Code Playgroud)
当您对列表中满足某些条件的第一个项目执行一些具有副作用的操作,然后立即从列表中删除该项目时,这通常比列表理解更容易理解。
这里的大多数答案都希望您创建列表的副本。我有一个用例,其中列表很长(110K 项),而继续减少列表会更明智。
首先,您需要用 while 循环替换 foreach 循环,
i = 0
while i < len(somelist):
if determine(somelist[i]):
del somelist[i]
else:
i += 1
Run Code Online (Sandbox Code Playgroud)
iif 块中的值不会更改,因为一旦删除旧项目,您将希望从相同的索引中获取新项目的值。
如果要在迭代时从列表中删除元素,请使用 while 循环,以便在每次删除后更改当前索引和结束索引。
例子:
i = 0
length = len(list1)
while i < length:
if condition:
list1.remove(list1[i])
i -= 1
length -= 1
i += 1
Run Code Online (Sandbox Code Playgroud)
如果您想在迭代期间执行其他操作,那么获取索引(这保证您能够引用它,例如,如果您有一个字典列表)和实际列表项内容可能会很好。
inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]
for idx, i in enumerate(inlist):
do some stuff with i['field1']
if somecondition:
xlist.append(idx)
for i in reversed(xlist): del inlist[i]
Run Code Online (Sandbox Code Playgroud)
enumerate让您可以立即访问该项目和索引。reversed这样您稍后要删除的索引就不会改变。
一种可能的解决方案,如果您不仅想删除某些内容,而且还想在单个循环中对所有元素执行某些操作,则该解决方案很有用:
alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
if x == 'bad':
alist.pop(i)
i -= 1
# do something cool with x or just print x
print(x)
i += 1
Run Code Online (Sandbox Code Playgroud)
for循环将迭代索引...
\n假设你有一个清单,
\n[5, 7, 13, 29, 65, 91]\nRun Code Online (Sandbox Code Playgroud)\n您使用了一个名为 的列表变量lis。你用同样的方法来删除...
你的变量
\nlis = [5, 7, 13, 29, 35, 65, 91]\n 0 1 2 3 4 5 6\nRun Code Online (Sandbox Code Playgroud)\n在第 5 次迭代期间,
\n您的数字 35不是素数,因此您将其从列表中删除。
\nlis.remove(y)\nRun Code Online (Sandbox Code Playgroud)\n然后下一个值 (65)移动到上一个索引。
\nlis = [5, 7, 13, 29, 65, 91]\n 0 1 2 3 4 5\nRun Code Online (Sandbox Code Playgroud)\n所以第四次迭代完成指针移到第五次......
\n这就是为什么你的循环不覆盖 65,因为它移到了上一个索引中。
\n因此,您不应该将列表引用到另一个仍然引用原始而不是副本的变量中。
\nite = lis # Don\xe2\x80\x99t do it will reference instead copy\nRun Code Online (Sandbox Code Playgroud)\n因此,使用 制作列表的副本list[::]。
现在你将给予,
\n[5, 7, 13, 29]\nRun Code Online (Sandbox Code Playgroud)\n问题是您在迭代期间从列表中删除了一个值,然后您的列表索引将崩溃。
\n所以你可以尝试列表理解。
\n它支持所有可迭代的,如列表、元组、字典、字符串等。
\n| 归档时间: |
|
| 查看次数: |
453310 次 |
| 最近记录: |