从踩坑中学习到python闭包
在搭建接口自动化框架的时候踩了一个坑。
1 场景
在循环中定义了一个函数作为动态创建函数的模板,通过循环将测试用例数据作为参数传入到该函数当中去执行。
2 问题
每次函数运行时的参数都是最后一次循环时传入的测试用例参数
3 原因
通过排查,由于函数是在循环内部定义的,所以所有的函数都引用了同一个测试用例数据变量(假如是A,引用的是变量地址,而不是变量的值),所以只会取循环结束时A的值。这是一个常见的 Python 闭包问题。
4 错误代码实例
将该问题抽象如下面的代码:
1 | class TestCase: |
在上述代码中,在循环内定义了一个方法,作为反射时用到的属性值,向TestCase
类中注入方法,本来的目的是想每个方法内输出不同的i
值。但是结果都输出了最后一次循环的取值。
1 | # 这是上面代码的控制台输出结果 |
5 解决方法
用嵌套函数来捕获循环中的变量
1 | class TestCase: |
1 | # 控制台输出结果如下 |
6 闭包
闭包(Closure)是编程中的一个重要概念,特别是在函数式编程中。闭包可以简单理解为能够记住并访问其词法作用域(lexical scope)的变量(即定义在其外部作用域而非全局作用域的变量)的函数。即使这个函数在其外部作用域被执行完并且返回之后,它仍然能够访问那些变量。
以下是闭包的一些关键点:
- 函数内部函数:闭包通常在一个函数内部定义另一个函数。
- 作用域链:内部函数可以访问其外部函数的变量,即使外部函数已经执行完毕。
- 返回内部函数:外部函数返回内部函数,使得内部函数可以在外部函数外部被调用,并且依然能够访问外部函数的变量。
- 延长变量生命周期:由于闭包的存在,那些原本在外部函数执行完毕后就应该被销毁的变量得以保存下来,从而延长了这些变量的生命周期。
闭包的使用场景包括:
装饰器:在不修改原有函数代码的情况下,增加额外的功能。
回调函数:封装了状态的函数可以作为回调函数传递给某些操作。
函数工厂:根据输入参数的不同返回不同行为的函数。
简单的闭包代码示例
1 | def outer_function(outer_variable): |
在这个例子中,outer_function
是一个外部函数,它定义了一个变量 outer_variable
和一个内部函数 inner_function
。inner_function
能够访问 outer_variable
,这是因为它在 outer_function
的作用域内被定义。当 outer_function
被调用时,它返回 inner_function
,此时 inner_function
就形成了一个闭包,因为它能够记住并访问 outer_variable
的值,即使 outer_function
已经执行完毕。