2

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

>>> 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进入循环时发生了什么?有什么要删除的吗?

4

5 回答 5

7

在任何for n in X语句的块内,n指的是名为n自身的变量,而不是“您迭代的最后一个值在列表中的位置”的任何概念。因此,您的循环所做的是将变量重复绑定n到从列表中获取的值,然后立即再次取消绑定相同的变量。该del n语句仅影响您的局部变量绑定,而不是列表。

于 2013-11-01T00:27:20.967 回答
2

因为你正在这样做:

some_reference = a[0]
del some_reference
#do you expect this to delete a[0]? it doesn't.

您正在对一个绑定到值的变量进行操作a[0](然后一个绑定到a[1],然后...)。您可以删除它,但它不会对a.

于 2013-11-01T00:32:53.390 回答
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

它也很慢:要找到ith 值,您必须检查i列表中的第一个元素。这将一个简单的线性(快速)算法变成了一个二次(慢)算法。

幸运的是,Python 有一个很好的工具来做你想做的事enumerate

for i, n in enumerate(a):
    print(i)

或者,如果您根本不需要这些值,只需索引:

for i in len(range(a)):
    print(i)

(这在文档中至少出现了两次提示,方便地埋在新手不会看到的地方。但它也在教程的早期进行了说明。)


接下来,您似乎正在尝试测试 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 未定义当您在迭代一个可迭代对象时对其进行变异时会发生什么——但它确实有一种特殊的语言来描述文档中内置可变序列(这意味着list)和s (我不记得在哪里,但它在某处说它不能保证提高 a ,这意味着它应该提高 a )。fordictRuntimeErrorRuntimeError

因此,如果您知道a是一个list,而不是某个子类list或第三方序列类,您可以在迭代时从中删除,并且如果您要删除的元素位于或到,则可以预期“跳过”行为迭代器的左边。但是很难想出这些知识的实际用途。

于 2013-11-01T23:08:27.987 回答
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!
于 2013-11-01T00:41:25.020 回答
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 都会移动到可迭代对象中的下一个相对位置。在这种情况下,n第一次引用a[0],第二次引用a[1],第三次引用a[2]。之后列表中没有 nextItem,因此迭代停止。

于 2013-11-01T04:34:11.697 回答