Kun*_*rma 92 python indexing for-loop
我有以下代码:
a = [0,1,2,3]
for a[-1] in a:
print(a[-1])
Run Code Online (Sandbox Code Playgroud)
输出为:
0
1
2
2
Run Code Online (Sandbox Code Playgroud)
我对为什么列表索引可以用作for循环中的索引变量感到困惑。
Tre*_*edJ 92
列表索引(例如a[-1]表达式for a[-1] in a中的索引)按for_stmt(特别是target_list)语法标记所指定slicing的有效,其中是有效的赋值目标。
“咦?分配?有什么是必须做的与我的输出是什么?”
确实,它与输出和结果有关。让我们进入一个for-in循环文档:
Run Code Online (Sandbox Code Playgroud)for_stmt ::= "for" target_list "in" expression_list ":" suite表达式列表将被评估一次;它应该产生一个可迭代的对象。为的结果创建一个迭代器
expression_list。然后,按照迭代器返回的顺序,对迭代器提供的每个项目执行一次套件。然后使用分配的标准规则将每个项目分配到目标列表(请参阅Assignment语句),然后执行套件。
(强调)
NB的套件引用的语句(S)的换块下,print(a[-1])在我们的具体情况。
让我们玩得开心,扩展print语句:
a = [0, 1, 2, 3]
for a[-1] in a:
print(a, a[-1])
Run Code Online (Sandbox Code Playgroud)
这给出以下输出:
[0, 1, 2, 0] 0 # a[-1] assigned 0
[0, 1, 2, 1] 1 # a[-1] assigned 1
[0, 1, 2, 2] 2 # a[-1] assigned 2
[0, 1, 2, 2] 2 # a[-1] assigned 2 (itself)
Run Code Online (Sandbox Code Playgroud)
(添加了评论)
在这里,a[-1]每次迭代都有变化,我们看到此变化传播到a。同样,由于slicing是有效目标,因此这是可能的。
Ev提出了一个很好的论据。Kounis使用上面引用的文档的第一句话:“ 表达式列表被评估一次 ”。这是否表示表达式列表是静态且不可变的,常量为[0, 1, 2, 3]?不应该a[-1]因此被分配3在最后一次迭代?
好吧,康拉德·鲁道夫断言:
不,[表达式列表]被评估一次以创建一个可迭代的对象。但是,该可迭代对象仍然在原始数据(而不是其副本)上进行迭代。
(添加了重点)
以下代码演示了可迭代的it 延迟生成列表元素的方式x。
x = [1, 2, 3, 4]
it = iter(x)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
x[-1] = 0
print(next(it)) # 0
Run Code Online (Sandbox Code Playgroud)
(代码受Kounis启发)
如果急切需要评估,我们可以期望x[-1] = 0对它的影响为零,it并希望4将其打印出来。显然不是这种情况,而是要表明,根据相同的原理,我们的for-loop懒惰地产生从每次迭代a后的赋值到的数字a[-1]。
Nat*_*han 24
(这比给出答案更像是一条漫长的评论-已经有好几个了,尤其是@ TrebledJ's。但是我不得不在覆盖变量之前就明确地考虑它,而对于我来说,这些变量已经有值了。)
如果你有
x = 0
l = [1, 2, 3]
for x in l:
print(x)
Run Code Online (Sandbox Code Playgroud)
您不会感到惊讶,x每次循环都将其覆盖。即使x以前存在,也不会使用它的值(即for 0 in l:,这将引发错误)。而是将值从分配l给x。
当我们做
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
Run Code Online (Sandbox Code Playgroud)
即使a[-1]已经存在并且具有值,我们也不会放入该值,而是a[-1]通过循环将其分配给每个时间。
blh*_*ing 11
for在每次迭代中,循环语句的左侧表达式都与右侧的iterable中的每个项目相关联,因此
for n in a:
print(n)
Run Code Online (Sandbox Code Playgroud)
只是一种幻想的方式:
for i in range(len(a)):
n = a[i]
print(n)
Run Code Online (Sandbox Code Playgroud)
同样
for a[-1] in a:
print(a[-1])
Run Code Online (Sandbox Code Playgroud)
只是一种幻想的方式:
for i in range(len(a)):
a[-1] = a[i]
print(a[-1])
Run Code Online (Sandbox Code Playgroud)
其中,在每次迭代中,的最后一项a分配为中的下一项a,因此,当迭代最终到达最后一项时,其值最后分配为倒数第二项2。
rec*_*nac 10
这是一个有趣的问题,您可以通过以下方式理解它:
for v in a:
a[-1] = v
print(a[-1])
print(a)
Run Code Online (Sandbox Code Playgroud)
实际上a变成:[0, 1, 2, 2]after loop
输出:
0
1
2
2
[0, 1, 2, 2]
Run Code Online (Sandbox Code Playgroud)
希望对您有所帮助,如有其他问题,请发表评论。:)
TrebledJ的答案解释了为什么可以做到这一点的技术原因。
您为什么要这样做?
假设我有一个对数组进行运算的算法:
x = np.arange(5)
Run Code Online (Sandbox Code Playgroud)
我想使用第一个索引的不同值来测试算法的结果。我可以简单地跳过第一个值,每次都重建一个数组:
for i in range(5):
print(np.r_[i, x[1:]].sum())
Run Code Online (Sandbox Code Playgroud)
(np.r_)
这将在每次迭代时创建一个新数组,这是不理想的,特别是在数组很大的情况下。要在每次迭代中重复使用相同的内存,我可以将其重写为:
for i in range(5):
x[0] = i
print(x.sum())
Run Code Online (Sandbox Code Playgroud)
哪个也可能比第一个版本更清晰。
但这与更紧凑的编写方式完全相同:
for x[0] in range(5):
print(x.sum())
Run Code Online (Sandbox Code Playgroud)
以上所有都会导致:
10
11
12
13
14
Run Code Online (Sandbox Code Playgroud)
现在,这是一个琐碎的“算法”,但是会有更复杂的用途,其中人们可能想要测试将数组中的单个(或多个,但由于赋值拆包而使事情复杂化)更改为多个值,最好不要复制整个数组。在这种情况下,您可能想在循环中使用索引值,但要做好准备,以防混淆任何维护您代码的人(包括您自己)。因此,x[0] = i最好使用显式分配的第二个版本,但是如果您更喜欢紧凑的for x[0] in range(5)样式,则这应该是一个有效的用例。
a[-1]a在本例中指的是 的最后一个元素a[3]。该for循环有点不寻常,因为它使用此元素作为循环变量。它不会在循环进入时评估该元素,而是在循环的每次迭代中分配给它。
因此首先a[-1]设置为 0,然后设置为 1,然后设置为 2。最后,在最后一次迭代中,for循环检索a[3]此时的2,因此列表最终为[0, 1, 2, 2]。
更典型的for循环使用简单的局部变量名称作为循环变量,例如for x ...。在这种情况下,x在每次迭代时设置为下一个值。这种情况没有什么不同,只是a[-1]在每次迭代时设置为下一个值。这种情况并不常见,但它是一致的。