[英]Why “changing list dynamically” behave differently in two level of nested loop?
我的问题是:
为什么它适用于内部循环,而不适用于外部循环?
不
如何写一些东西来完成这项工作...我知道在循环中动态更改列表不是一个安全的解决方案。
代码1:
list_a = [1,2,3]
list_b = [11,12,13]
for i in list_a:
for j in list_b:
print(i, j)
结果1:
1 11
1 12
1 13
2 11
2 12
2 13
3 11
3 12
3 13
Code 1
是用于打印两个列表的组合的两级嵌套循环,它可以按预期工作。
我想在循环过程中动态更改列表。 通过更改触发循环的列表,我希望循环行为也可以动态更改。
代码2:
list_a = [1,2,3]
list_b = [11,12,13]
for i in list_a:
list_a = [100, 200]
for j in list_b:
print(i, j)
代码3:
list_a = [1,2,3]
list_b = [11,12,13]
for i in list_a:
for j in list_b:
list_a = [100, 200]
print(i, j)
结果2/3:
1 11
1 12
1 13
2 11
2 12
2 13
3 11
3 12
3 13
Code 2
和Code 3
用于更改第一个列表。 尽管list_a在循环的第一步中已更新,但外部for loop
的循环行为不会改变。
代码4:
list_a = [1,2,3]
list_b = [11,12,13]
for i in list_a:
for j in list_b:
list_b = [100, 200]
print(i, j)
结果4:
1 11
1 12
1 13
2 100
2 200
3 100
3 200
Code 4
用于更改第二个列表。 list_b在循环的第一步中更新,并且内部循环的行为受到影响。
问题在于for
循环会在入口处创建并绑定其迭代器,而不必重新绑定它们以后的名称也没关系。 想象一下这种形式的for
循环:
for i in list_a:
像这样实现:
_unnamed_iter_ = iter(list_a)
while True:
try:
i = next(_unnamed_iter_)
except StopIteration:
break
请注意,在循环期间,它永远不会查看list_a
; 它从循环开始之前由绑定到list_a
绑定的内容的迭代器读取,但是将list_a
重新分配给新 list
,而不是修改它当前绑定的list
这不会更改迭代器的外观。 修改当前绑定的list
将是 “有效的”,尽管它显然违反了规则(在迭代集合的同时修改集合有时是可行的,但通常是一些细微的错误的来源)。 使代码按预期工作的一种简单方法是进行更改:
list_a = [100, 200]
至:
list_a[:] = [100, 200]
切片分配将替换绑定到list_a
的list
的内容 ,它不会将其重新绑定到全新的list
,因此原始list
支持的迭代器将按预期进行修改。
代码4之所以起作用,是因为您多次遍历list_b
(因为开始循环不止一次,并且每次循环时都会list_b
“绑定到list_b
”创建一个新的迭代器)。 您会注意到重新分配并没有更改list_b
的第一个循环(因为迭代器继续使用旧的list
),但是在第二个循环(具有list_a
的第二个值)上,它创建了一个新的迭代器,该迭代器基于list_b
的新绑定值。
无论如何,这都是毫无意义的肚脐注视。 您所做的任何事都不是安全或理智的,并且“修复”(切片分配)很可能在其他Python解释器(甚至可能在将来的解释器版本)中中断。
Python具有广泛的范围。 与某些为每个if语句,for语句等创建新作用域的语言相反,Python仅具有三层作用域:局部作用域,其闭合和全局作用域。
# global scope
a = 1
def f():
# closure of g
b = 2
def g():
# local scope of g
c = 3
print(
a, # get a from the global scope
b, # get b from the closure
c # get c from the local scope
)
g()
f() # prints: 1 2 3
例如,请考虑以下JavaScript代码段。
// JavaScript
let x = [1, 2]
for (i = 0; i < 3; i++) {
let x = [3, 4] // The use of let declares are new variable specific to this scope
console.log(x) // [3, 4]
}
console.log(x) // [1, 2]
我们在这里注意到的是,for循环具有自己的范围。 许多知名语言就是这种情况。 虽然,在Python中不会发生相同的情况。
# Python3
x = [1, 2]
for i in x:
x = [3, 4]
print(x) # [3, 4]
print(x) # [3, 4]
请注意,循环内x
的分配是如何从循环中泄漏出来的。
让我们回顾一下您的示例。 下面的所有代码都在同一范围内发生。
list_a = [1 ,2, 3]
list_b = [11, 12, 13]
for i in list_a:
for j in list_b:
list_b = [100, 200] # This makes the name 'list_b' point to the value [100, 200]
print(i, j)
# Here the name 'list_b' is not set back to [11, 12, 13] since we are in the same scope
由于所有事情都在同一范围内发生,因此在list_a
的第二次迭代中,名称list_b
现在指向一个新值: [100, 200]
。
由于作用域在Python中的工作原理,在迭代过程中更改可迭代对象不仅是不好的做法,而且重用它的名称也是不好的做法。 因此,您想对在for循环内定义的变量使用新名称,因为这些变量会从循环中泄漏出来。
list_a = [1 ,2, 3]
list_b = [11, 12, 13]
current_iterable = list_b
for i in list_a:
for j in current_iterable:
print(i, j)
current_iterable = [100, 200]
print(list_b) # [11, 12, 3]
print(current_iterable) = [100, 200]
如您所见, current_iterable
名称仍然存在于循环外部,但是至少我们没有使用仅由循环使用的值覆盖已经存在的名称。
这当然是一个经验法则,有时我们可能想要增量更新一个可迭代对象。
def breadth_first_search(initial_node):
nodes = [initial_node]
for node in nodes:
node.seen = True
queue.extend(n for n in node.neighbors if not n.seen)
return nodes
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.