作用域与名字空间提到,python是静态作用域,变量定义的位置决定了变量作用的范围。变量沿着local,global,builtins的路径搜索,直觉上就是从里到外搜索变量,这称为最内嵌套作用域规则。

从里到外的搜索

 a = 1

 def f():
a = 2
def b():
print(a)
b() f()

输出结果

 2

最内嵌套作用域规则有一个神奇的特性,它对local变量的搜索只依赖于静态代码的组成,而与代码如何调用没有关系。

闭包

 a = 1

 def f():
a = 2
def b():
print(a)
return b b = f()
b()

输出结果

 2

函数f返回函数b,在第九行调用f得到函数b,此时函数f调用完成,应该被销毁,它包含的局部变量a也应该随之销毁。所以调用函数b时应该得不到函数f的变量a才对,事实却相反,函数b打印出了本应被销毁的a变量。

局部作用域是静态实现的,跟代码如何调用,何时调用没有关系。对于局部变量a来说,它的作用域是函数f内,当然包含了函数b。而且在函数b中确实引用了a,为了让b在任何时候都能够得到它,a会将自己绑定到函数b上。

这样b得到了一个额外的变量a,在函数f被销毁后,仍然能够使用变量a。这种变量与函数绑定的结果称为闭包(closure),这种绑定的变量是静态变量(类似于为函数添加了属性)。

直观上,闭包的作用就是为函数添加静态变量。

函数计数器

 def fn():
print("call fn") def count(fn):
i = [0]
def s(*arg):
i[0] += 1
print(f"times: {i[0]}")
fn(*arg)
return s fn = count(fn)
for _ in range(5):
fn()

输出结果

 times: 1
call fn
times: 2
call fn
times: 3
call fn
times: 4
call fn
times: 5
call fn

有了闭包,不用传入额外的参数,函数自身就能记住状态的变化(闭包提供静态变量)。

装饰器

 def count(fn):
i = [0]
def s(*arg):
i[0] += 1
print(f"times: {i[0]}")
fn(*arg)
return s @count
def fn():
print("call fn") for _ in range(5):
fn()

装饰器其实就是一个语法糖,fn上方加@count,等价于fn = count(fn)。

计数功能仍靠闭包实现。

@count必须出现在count定义函数之后,否则无法识别。

总结:

1. 最内嵌套作用域对于local变量的搜索只与代码组成有关,与动态运行环境无关

2. 闭包是外层变量与内层函数的绑定结果,主要作用是位函数添加静态变量

3. 装饰器是语法糖,装饰函数一般都返回一个闭包。

python tips:最内嵌套作用域规则,闭包与装饰器的更多相关文章

  1. 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!

    Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...

  2. python 基础之第十天(闭包,装饰器,生成器,tarfile与hashlib模块使用)

    局部变量与全局变量 局部变量:在函数里面定义的,只有当函数活动时才生效 全局变量:不在函数里面的 In [1]: x=10 In [2]: def bar(): ...: x=20 ...: prin ...

  3. Python--高阶函数、函数嵌套、名称空间及变量作用域、闭包、装饰器

    1.高阶函数(map/reduce/filter) 高阶函数是指函数的参数可以是函数 这篇总结几个常用的高阶函数:map/reduce/filter map函数.reduce函数.filter函数都是 ...

  4. Python 变量作用域,闭包和装饰器

    from dis import dis b = 6 def f1(a): print(a)print(b) b = 9 f1(3) print(dis(f1)) # dis模块可以查看python函数 ...

  5. 周末学习笔记——day01(函数,函数对象,嵌套调用,名称空间,作用域,闭包,装饰器)

    一,复习 字符编码 文件头:py2—ASCII,py3—UTF-8 三种字符串:u' ' b ' ' r ' ' u' ' .endcode(' utf-8 ') b' '.deconde(' utf ...

  6. Python 名称空间与作用域、闭包与装饰器

    Python 的名称 Python 的名称(Name)是对象的一个标识(Identifier).我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的.说得有点玄乎,我们以例子说明. 例如 ...

  7. python函数作用域,闭包,装饰器

    第一:函数作用域: L:local 函数内部作用域 E:enclosing       函数内部与内嵌函数之间(闭包) G:global            全局作用域 B:build_in    ...

  8. python中的生成器、迭代器、闭包、装饰器

    迭代器 迭代是访问集合元素的一种方式.迭代器是一个可以记住遍历的位置的对象.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 可迭代对象 以直接作用于 for ...

  9. Python进阶(五)----内置函数Ⅱ 和 闭包

    Python进阶(五)----内置函数Ⅱ 和 闭包 一丶内置函数Ⅱ ####内置函数#### 特别重要,反复练习 ###print() 打印输入 #sep 设定分隔符 # end 默认是换行可以打印到 ...

随机推荐

  1. supervisord进程管理工具小结

    前言 昨天临近下班,发现业务队列处理不及时,正好想到不久之前,上了一个新功能:通过队列异步转发微信消息.可能是消息太多了处理不过来,没怎么多想,处理不过来了,多增加处理进程就可以了,后来发现自己so ...

  2. mybatis sql语句#{}和${}区别联系

    1.说白了就是,#{}用于引用字符变量,如varchar,string.因为sql语句执行过程中要给string varchar加‘’来执行. 2.${}用来引用int型等不需要添加单引号的值 3.具 ...

  3. [jQuery]ajax请求导致浏览器崩溃

    $("#xxx").val() not $("#xxx") 如果忘记加上.val()会导致chrome崩溃

  4. iOS:制作左右侧滑(抽屉式)菜单

    感谢控件作者:https://github.com/SocialObjects-Software/AMSlideMenu 首先上效果图: 这里我们使用AMSlideMenu来实现左右侧滑菜单的效果.控 ...

  5. sleep与信号唤醒的问题 & 内核对信号的处理方式

    注意,sleep是会被信号唤醒的.   sleep函数:#include <unistd.h>unsigned int sleep(unsigned int seconds);此函数使调用 ...

  6. 【原创】TCP超时重传机制探索

    TCP超时重传机制探索 作者:tll (360电商技术) 1)通信模型 TCP(Transmission Control Protocol)是一种可靠传输协议.在传输过程中当发送方(sender)向接 ...

  7. 我的Android进阶之旅------&gt;Android中ListView中嵌套(ListView)控件时item的点击事件不起作的问题解决方法

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  8. dlk 模板

    struct DLX { int n , m,size; int U[N],D[N],R[N],L[N],row[N],col[N]; int ans[N],S[N]; int ansd; void ...

  9. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  10. WebSocket在Asp.Net中的例子

    环境 以下代码环境要求:win8或win10, .net4.5+IIS8 部署到IIS8上面 转到 Windows程序和功能 -打开Windows功能里面 IIS选项启动4.5 和WebSocket支 ...