代码调试结果再一次被自己已有的认知打败,恶补基础,今天扯一扯python的闭包及装饰器.
有意思的代码
1 | def f(): |
大家很快能得到答案: [0,2,4,6]
,但是这真的对吗?
正确答案为[6,6,6,6]
,我也一脸蒙蔽: But How?
首先可以肯定的是函数f返回一个列表解析,列表里的对象是4个lambda对象
当使用f()调用f函数时,f返回了一个列表,列表里也确实是4个lambda对象,这个问题的关键在于i的值,对于lambda来说,变量i的值 永远都是for循环里最后一次i的值,也就是3,4个lambda都是如此
再看这段代码
1 | data = range(4) |
Python的闭包(lambda和function等)会保存变量的名字(i)和scoping(global)。当你调用的时候才会去找具体的对象。所以你给i赋不同的值,在那之后的调用都会去取新的i = 21,for i in data中的i值显然不是最新值.
python闭包
python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
1 | def func1(args): |
inner_func中使用了args,但是inner_func中并没有定义args,此时python解析器会向上查找,在func1找到使用.
全局作用域无法访问局部作用域,而局部作用域能够访问全局作用域就这这个原因。而当我在局部作用域创建了一个和外面同名的变量时,python在找这个变量的时候首先会在当前作用域中找,找到了,就不继续往上一级找了
当函数存在嵌套,并且子函数引用了父函数中的变量,可以访问这些变量的作用域就形成闭包,如果子函数没有访问父函数中的变量,就不存在闭包
内部函数inner_func引用了外部func1的变量,所有inner_func是闭包.
那第一段代码如何才能得到结果为[0,2,4,6]
呢,可以这样
1 | #方法一: |
闭包的最常用的使用场景即是装饰器
python装饰器
因为python中一切皆对象,函数也是对象,所以也可以将函数做为参数传递,我们将上面的例子改一改,将函数做为参数传递
1 | def func1(func): |
应该就能明白装饰器是什么了.所谓装饰器就是在闭包的基础上传递了一个函数,然后覆盖原来函数的执行入口,以后调用这个函数的时候,就可以额外实现一些功能了.装饰器的存在主要是为了不修改原函数的代码,也不修改其他调用这个函数的代码,就能实现功能的拓展.
当然装饰器还有更简介的写法,使用@语法糖
1 | def func1(func): |
装饰器还有很多其它特性,下次再另起一篇,这里算是开个头,困了,就到这里吧.