当我循环遍历列表时,我在循环中给列表元素的名称显然直接指向每个元素,如下所示:
>>> a = [1, 2, 3]
>>> for n in a:
... print n is a[a.index(n)]
True
True
True
所以,纯粹出于好奇,为什么这似乎没有做任何事情?
>>> for n in a: del n
>>> a
[1, 2, 3]
如果我尝试del a[a.index(n)]
,我会得到不稳定的行为,但至少它是我能理解的行为 - 每次删除元素时,我都会缩短列表,更改其他元素的索引,所以我最终删除列表中的每个其他元素:
>>> a = range(10)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for n in a: del a[a.index(n)]
>>> a
[1, 3, 5, 7, 9]
显然我可以在迭代时从列表中删除。那么当我在循环中尝试del n
时会发生什么?有什么东西被删除了吗?
再次:纯粹是出于好奇。收起你的干草叉。
答案 0 :(得分:7)
在任何for n in X
语句的块中,n
引用名为n
的变量本身,而不是“您迭代的最后一个值列表中的位置”的任何概念”。因此,循环正在做的是将变量n
重复绑定到从列表中获取的值,然后立即再次解除绑定相同的变量。 del n
语句仅影响本地变量绑定,而不是列表。
答案 1 :(得分:2)
因为你这样做:
some_reference = a[0]
del some_reference
#do you expect this to delete a[0]? it doesn't.
您正在操作一个绑定到a[0]
值的变量(然后绑定到a[1]
,然后......)。您可以删除它,但它不会对a
执行任何操作。
答案 2 :(得分:1)
其他人已经解释了很好地删除引用的想法,我只想触摸项目删除的完整性。即使您使用类似的语法del a[1]
,每种类型也会略微区别地处理项目删除。正如所料,从大多数容器中删除项目只会删除它们,而某些类型根本不支持项目删除,例如元组。就像一个有趣的锻炼者:
class A(object):
def __delitem__(self, index):
print 'I will NOT delete item {}!'.format(index)
a = A()
del a[3]
# I will NOT delete item 3!
答案 3 :(得分:1)
Dolda2000的回答非常清楚地涵盖了主要问题,但这里还有其他三个问题。
在index(n)
循环中使用for n in a
几乎总是一个坏主意。
这是不正确的:
a = [1, 2, 1, 2]
for n in a:
print(a.index(n))
这将再次打印0
,然后是1
,然后是0
。为什么?好吧,第三个值是1
。 a.index(1)
是列表中第一个1
的索引。那是0
,而不是2
。
它也很慢:要查找i
值,您必须检查列表中的第一个i
元素。这将简单的线性(快速)算法转换为二次(慢)算法。
幸运的是,Python有一个很好的工具可以完全按照你想要的方式进行,enumerate
:
for i, n in enumerate(a):
print(i)
或者,如果您根本不需要这些值,只需索引:
for i in len(range(a)):
print(i)
(这在文档中显示为至少两次暗示,方便地埋没在新手看不到的地方。但它也拼写出early in the tutorial。)
接下来,看起来你正试图测试Dolda2000解释发生的情况,其中包括:
n is a[a.index(n)]
为什么没有这个工作?你证明它们是同一个对象,为什么不删除它呢?
与C系列语言不同,其中变量是存储值(包括对其他地址的引用)的地址,Python变量是绑定到其他地方存在的值的名称。因此变量不能引用其他变量,但它们可以是相同值的名称。 is
表达式测试两个表达式是否命名相同的值。所以,你证明你有两个相同值的名字,你删除了其中一个名字,但另一个名称和值仍在那里。
一个例子值1000字,所以运行:
a = object() # this guarantees us a completely unique value
b = a
print a, b, id(a), id(b), a is b
del b
print a, id(a)
(当然如果你也del a
,那么在某些时候Python 将删除该值,但是你看不到它,因为根据定义你不再有任何名字了看看它。)
好吧,有点儿。 Python将它定义为未定义当迭代迭代时迭代它时会发生什么 - 但它确实有特殊的语言描述了显然,我可以在迭代时从列表中删除。
for
文档中内置可变序列(这意味着list
)的内容,以及对于dict
s(我不记得在哪里,但它在某个地方说它不能保证引发RuntimeError
,这意味着它应该引发一个{{ 1}})。
因此,如果您知道 RuntimeError
是a
,而不是list
或第三方序列类的某些子类,那么 允许在迭代时从中删除,并且如果要删除的元素位于迭代器的左侧或者在迭代器的左侧,则期望“跳过”行为。但是很难想到这种知识的现实用途。
答案 4 :(得分:0)
当你这样做时会发生这种情况
for n in a: del a[a.index(n)]
试试这个:
a = [0, 1, 2, 3, 4]
for n in a: print(n, a); del a[a.index(n)]
这就是你得到的:
(0, [0, 1, 2, 3, 4])
(2, [1, 2, 3, 4])
(4, [1, 3, 4])
因此,n只是索引的跟踪器,您可以这样思考。每次函数迭代时,n都会移动到iterable中的下一个相对位置。 在这种情况下,n第一次引用[0],第二次引用[1],第三次引用[2]。之后,列表中没有nextItem,因此迭代停止。