循环变量捕获¶
ID: py/loop-variable-capture
Kind: problem
Security severity:
Severity: error
Precision: high
Tags:
- correctness
Query suites:
- python-security-and-quality.qls
嵌套函数是 Python 的一个有用功能,因为它允许函数访问其封闭函数的变量。但是,程序员需要意识到,当内部函数访问外部作用域中的变量时,它捕获的是变量本身,而不是该变量的值。
因此,在捕获的变量是循环变量时,需要格外小心,因为捕获的是循环变量本身,而不是该变量的值。这意味着当内部函数执行时,循环变量将具有其最终值,而不是创建内部函数时的值。
建议¶
解决此问题的最简单方法是添加一个与外部变量同名的局部变量,并使用外部变量作为默认值对其进行初始化。`for var in seq: ... def inner_func(arg): ... use(var)` 变为 `for var in seq: ... def inner_func(arg, var=var): ... use(var)`
示例¶
在此示例中,创建了一个函数列表,每个函数都应该将其参数增加列表中其索引的值。但是,由于 `i` 在函数执行时将为 9,因此它们都将将其参数增加 9。
#Make a list of functions to increment their arguments by 0 to 9.
def make_incrementers():
result = []
for i in range(10):
def incrementer(x):
return x + i
result.append(incrementer)
return result
#This will fail
def test():
incs = make_incrementers()
for x in range(10):
for y in range(10):
assert incs[x](y) == x+y
test()
这可以通过添加默认值来解决,如下所示。默认值是在创建函数时计算的,因此可以实现所需的效果。
#Make a list of functions to increment their arguments by 0 to 9.
def make_incrementers():
result = []
for i in range(10):
def incrementer(x, i=i):
return x + i
result.append(incrementer)
return result
#This will pass
def test():
incs = make_incrementers()
for x in range(10):
for y in range(10):
assert incs[x](y) == x+y
test()