对于列表中的n:del n

时间:2013-11-01 00:25:06

标签: python loops scope

当我循环遍历列表时,我在循环中给列表元素的名称显然直接指向每个元素,如下所示:

>>> 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时会发生什么?有什么东西被删除了吗?

再次:纯粹是出于好奇。收起你的干草叉。

5 个答案:

答案 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。为什么?好吧,第三个值是1a.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}})。

因此,如果您知道 RuntimeErrora,而不是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,因此迭代停止。