我知道我不应该在循环中修改列表,但出于好奇,我想知道为什么以下两个示例之间的迭代次数不同。
示例1:
x = [1, 2, 3, 4, 5]
for i, s in enumerate(x):
del x[0]
print(i, s, x)
示例2:
x = [1,2,3,4,5]
for i, s in enumerate(x):
x = [1]
print(i, s, x)
示例1仅运行3次,因为i==3
,len(x)==2
。
即使len(x)==1
,例2也会运行5次。
所以我的问题是,enumerate
是否在循环开始时生成(index, value)
对的完整列表并迭代它?或者它们是在循环的每次迭代中生成的?
答案 0 :(得分:22)
在第一个示例中,您实际上正在修改正在迭代的列表。
另一方面,在第二种情况下,您只是将新对象分配给名称x
。但循环迭代的对象不会改变。
有关Python中名称和变量的更详细说明,请查看http://foobarnbaz.com/2012/07/08/understanding-python-variables/。
答案 1 :(得分:18)
enumerate()返回迭代器或其他支持迭代的对象。 __next__()返回的迭代器的enumerate()方法返回一个包含计数的元组(从start开始,默认为0)和从迭代 iterable 获得的值。
__next__()返回容器中的下一个项目。如果没有其他项目,请提出StopIteration
例外。
enumerate()是否在循环开始时生成(索引,值)对的完整列表并迭代它?或者它们是在循环的每次迭代中生成的?
因此,enumerate()
返回一个迭代器,并且在每次迭代时,__next__()
都会检查是否还有其他项。 enumerate()
在循环开始时不会创建完整列表。
正如@Wisperwind提到的那样,在您的第二种情况下,您要为名称x
分配新对象。循环遍历的对象在迭代期间不会改变。
答案 2 :(得分:8)
只是澄清了Wasi Ahmad和Wisperwind所说的话。两者都声明“您只是将新对象分配给名称x”。这可能会有点混乱,因为它可能被解释为“你正在创建一个新对象([1]
)并将其存储到名称x
,你会说”嗯,所以为什么不改变?!“要看看发生了什么,打印出对象的id
x = [1, 2, 3, 4, 5]
y = x # To keep a reference to the original list
print id(x), id(y)
for i, v in enumerate(x):
x = [1]
print id(x), id(y)
print id(x), id(y)
# output (somewhat contrived as I don't have a python environment set up)
# X ID Y ID
10000000000001 10000000000001
10000000000002 10000000000001
10000000000003 10000000000001
10000000000004 10000000000001
10000000000005 10000000000001
10000000000006 10000000000001
10000000000006 10000000000001
您会注意到id
的{{1}}每次都在循环中发生变化,当您完成循环后,x
将指向最后一次修改循环。当你完成循环时,它会迭代x的原始实例,无论你是否仍然可以引用它。
如您所见,x
指向原始y
。当您在循环中进行迭代时,即使x
正在发生变化,x
仍然指向仍在循环的原始y
。
答案 3 :(得分:1)
确实:您的第一个代码段修改了迭代列表;第二个将变量x
指向一个新列表,保留未经修改的enumerate()
横向列表。您可以通过访问www.pythontutor.com上的以下链接来查看此操作,该链接允许您单步执行代码并可视化变量的内容:
First version(x
已修改)。
Second version(x
被重定向到[1]
)。
为了更好地了解正在进行的操作,请转到here,然后跳过以下扩展代码:
x = [1,2,3,4,5]
view = enumerate(x)
for i, s in view:
x = [1]
print(i, s, x)
答案 4 :(得分:1)
其他人已经指出,您的第二个示例只会更改x
点的值,而不会更改您重复的列表。这是普通作业(x = [1]
)和slice assignment(x[:] = [1]
)之间差异的完美示例。后者将列表x
修改为就地:
x = [1, 2, 3, 4, 5]
for i, s in enumerate(x):
x[:] = [1]
print(i, s, x)
将打印
(0, 1, [1])
答案 5 :(得分:0)
x = [1, 2, 3, 4, 5]
列表[1, 2, 3, 4, 5]
已标记为x
for i, s in enumerate(x):
enumerate()附加了另一个标记,因此[1, 2, 3, 4, 5]
现已标记为x
和y
。 enumerate()将继续使用y
标记,而不是x
标记。
del x[0]
存储在内存中的列表已修改,因此x
和y
现在都引用[2, 3, 4, 5]
或者,当您使用
时x = [1]
在内存中创建了一个新列表[1]
,x
标记现在指向该列表。 y
标记仍指向原始列表。
Python变量如何工作:
http://foobarnbaz.com/2012/07/08/understanding-python-variables/