CodeQL 文档

循环变量捕获

ID: py/loop-variable-capture
Kind: problem
Security severity: 
Severity: error
Precision: high
Tags:
   - correctness
Query suites:
   - python-security-and-quality.qls

点击查看 CodeQL 代码库中的查询

嵌套函数是 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()

参考资料

  • ©GitHub, Inc.
  • 条款
  • 隐私