python中函数嵌套、函数作为变量以及闭包的原理
嵌套函数:
python允许创建嵌套函数。也就是说我们可以在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。
例子:
#encoding=utf-8
def outer():
name="python"
def inner():#outer函数内部定义的函数
print name
return inner()#返回该内部函数
outer()
结果:
理解:
在inner函数中,python解析器需要找一个叫name的本地变量,查找失败后会继续在上层的作用域里面寻找,这个上层作用域定义在outer函数里,python函数可以访问封闭作用域。
对于outer函数中最后一句,返回inner函数调用的结果,需要知道非常重要一点就是,inner也仅仅是一个遵循python变量解析规则的变量名,python解释器会优先在outer的作用域里面对变量名inner查找匹配的变量。
把恰好是函数标识符的变量inner作为返回值返回回来,每次函数outer被调用的时候,函数inner都会被重新定义,如果它不被当做变量返回的话,每次执行过后它将不复存在。
在python里,函数就是对象,它也只是一些普通的值而已。也就是说你可以把函数像参数一样传递给其他的函数或者说从函数了里面返回函数
return内层函数时不加括号,只返回函数的地址:
代码:
#encoding=utf-8
def outer():
name="python"
def inner():#outer函数内部定义的函数
return name
return inner#返回该内部函数
print outer()
结果:
此时执行外层函数outer(),返回的是内层函数的函数引用(函数名称)--inner,想要执行内层函数,需要在outer()后边再加个括号,即outer()(),才会让内层函数执行
代码:
#encoding=utf-8
def outer():
name="python"
def inner():#outer函数内部定义的函数
return name
return inner#返回该内部函数
print outer()()
结果:
函数作为变量:
例子:
#encoding=utf-8
def add(x,y):
return x+y
def sub(x,y):
return x-y
def apply(func,x,y):
return func(x,y)
print "apply(add,2,1):",apply(add,2,1)
print "apply(sub,2,1):",apply(sub,2,1)
结果:
apply函数准备接收一个函数的变量,它也只是一个普通的变量而已,和其他变量一样。然后我们调用传进来的函数:“()代表着调用的操作,并且调用变量包含的值”。
在函数外,我们也能看到传递函数并没有什么特殊的语法,函数的名称只是和其他变量一样的表标识符而已。
闭包的理解:
先看个例子
#encoding=utf-8
def outer():
name="python"
def inner():
print name
return inner
res=outer()
res()
print res.func_closure#打印闭包里包含哪些外部变量
结果:
例中,inner作为一个函数被outer返回,保存在变量res中,并且还能够调用res()。为什么能调用呢?
通过上面变量的作用域和生存周期我们不难明白,name是函数outer里的一个局部变量,也就是说只有当outer正在运行时,该变量才会存在。
根据python的运行模式,我们是没法在函数outer执行退出之后还能继续调用inner函数的,并且在inner函数被调用时,变量name早已不存在了,但是为什么我们调用成功了呢?
这就回到了我们的闭包这个问题上了,python支持一个叫函数闭包的特性。
啥是闭包?
如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包。闭包是Python所支持的一种特性,它让在非global scope定义的函数可以引用其外围空间中的变量,这些外围空间中被引用的变量叫做这个函数的环境变量。环境变量和这个非全局函数一起构成了闭包。
上例中的inner()函数就是一个闭包,它本身也是一个函数,而且还可以访问本身之外的变量。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如name,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)
每次函数outer被调用时,inner函数都会被重新定义,上面每次返回的函数inner结果都一样,因为name没变。
如下例所示,我们将函数稍微改动一下,结果就不一样了
代码:
#encoding=utf-8
def outer(name):
def inner():
print name
return inner
res1=outer("python")#返回闭包
res2=outer("java")#返回闭包
res1()#执行函数
res2()
结果:
分析:
在之前的例子中
def outer():
name="python"
def inner():
print name
return inner
外层函数后运行后,返回一个函数+函数需要的变量
name = "python"
def inner() :
print name
上边三行是整体返回的内容
如果在外层函数再加个外部的整形变量,在里面的函数中引用:
#encoding=utf-8
def outer():
name="python"
nbr=12
def inner():
print name
print nbr
return inner#不加括号就是返回函数对象,不是函数调用
res=outer()
res()#调用inner函数
print res.func_closure#打印闭包里包含哪些外部变量
结果:
D:\>python test.py
python
12
(<cell at
0x0000000004E56CA8: str object at 0x0000000004E58760>, <cell at
0x0000000004E56F78: int object at 0x0000000004E362C0>)
其中res = outer()是把inner函数对象赋值给res,因为没有括号,所以不是调用而是返回个函数对象
res()是调用inner函数
print res.func_closure是打印闭包里包含哪些外部变量,可以看到结果里有两个:即python和12
(<cell at 0x0000000004E56CA8: str object at 0x0000000004E58760>, <cell at 0x0000000004E56F78: int object at 0x0000000004E362C0>)
闭包特点:
一个函数返回的函数对象,这个函数对象执行的话依赖非函数内部的变量值,这个时候,函数返回的实际内容如下:
1 函数对象
2 函数对象需要使用的外部变量和变量值
以上就是闭包
闭包必须嵌套在一个函数里,必须返回一个调用外部变量的函数对象,才是闭包
在上边的例子中,相对于inner来说 ,outer函数就是它得全局变量,就好像你存粹写个函数会用到函数外面环境定义得全局变量一样 ,都是相对的概念
通俗理解就是:里面函数执行 ,需要用到外面函数的一个变量 ,所以,就把外面变量和里面这个函数合到一块,合到一块的这两个东西就是闭包
python中函数嵌套、函数作为变量以及闭包的原理的更多相关文章
- [转]关于python中带下划线的变量和函数的意义
Python 的代码风格由 PEP 8 描述.这个文档描述了 Python 编程风格的方方面面.在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格.这样就易于阅读, ...
- python中如何调用函数交换两个变量的值
python中如何调用函数交换两个变量的值 所有代码来在python3.7.1版本实现 以下实例通过用户输入两个变量,并相互交换: 方法一: def swap(a,b): # 创建临时变量,并交换 ...
- python中的生成器函数是如何工作的?
以下内容基于python3.4 1. python中的普通函数是怎么运行的? 当一个python函数在执行时,它会在相应的python栈帧上运行,栈帧表示程序运行时函数调用栈中的某一帧.想要获得某个函 ...
- 举例详解Python中的split()函数的使用方法
这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下 函数:sp ...
- python中的map()函数
MapReduce的设计灵感来自于函数式编程,这里不打算提MapReduce,就拿python中的map()函数来学习一下. 文档中的介绍在这里: map(function, iterable, .. ...
- 揭秘 Python 中的 enumerate() 函数
原文:https://mp.weixin.qq.com/s/Jm7YiCA20RDSTrF4dHeykQ 如何以去写以及为什么你应该使用Python中的内置枚举函数来编写更干净更加Pythonic的循 ...
- (转)Python中的split()函数的用法
Python中的split()函数的用法 原文:https://www.cnblogs.com/hjhsysu/p/5700347.html Python中有split()和os.path.split ...
- Python 函数对象-函数嵌套-名称空间与作用域-闭包函数
今日内容: 1. 函数对象 函数是第一类对象: 指的是函数名指向的值可以被当中数据去使用 1.可以被引用 2.可以当做参数传给另一个函数 3.可以当做一个函数的返回值 4.可以当做容器类型的元素 2. ...
- Python中的lambda函数介绍
Lambda函数,即Lambda 表达式(lambda expression),是一个匿名函数(不存在函数名的函数),Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lam ...
随机推荐
- Matlab中添加路径与去除路径
今天在使用Matlab调用内部的PCA函数的时候,报错: 错误使用 pca输入参数太多. 如下图所示: 网上查找原因之后发现是因为我之前下载过开源的工具包toolbox,并且将它的路径add到了Mat ...
- MongoDB的客户端管理工具--nosqlbooster 查询工具使用
连接我的MongoDB 看到这样 打开db1数据库里面user集合,看到user集合里面的数据,他会自带查询语句 看这里以tree方式显示 可以以table方式显示 还可以json方式显示 按照自己的 ...
- dedecms首页去掉index.html怎么设置
很多网友用IIS服务器建站,反映说dedecms首页默认多了一个/index.html,一般是没有这个后缀的,直接就**.com,那么如何将dedecms首页去掉index.html呢?很简单,服务器 ...
- javascript篇-typeof,instanceof,constructor,toString判断数据类型的用法和区别
javascript基本数据类型有:string,number,Boolean,undefined,null 引用类型(复杂类型):object, ES6中新增了一种数据类型:Symbol 以上数据类 ...
- [vue]声明式导航和编程式导航
声明式导航和编程式导航 共同点: 都能进行导航,都可以触发路由,实现组件切换 区别: 写法不一样,声明式导航是写在组件的template中,通过router-link来触发,编程式导航写在js函数中, ...
- DAX/PowerBI系列 - 参数表(Parameter Table) - 大客户分析(Top N)
DAX/PowerBI系列 - 参数表(Parameter Table) - 大客户分析(Top N) 难度: ★☆☆☆☆(1星) 适用范围: ★★★☆☆(3星) 概况:此文为DAX/PowerBI系 ...
- Go linux 实践 1
引言: 如果,曾经,你以作为一名C语言应用开发者而自豪,那么后来你应该以用C++来开发为时髦,当JAVA出现时,你可能会说“这小子,有两下子嘛!” 但是,当你以JAVA专家出厂时,哈哈,返过头来面对J ...
- Linux基础(三)Shell test 命令
Shell test 命令 Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值.字符和文件三个方面的测试. 数值测试 参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt ...
- Oracle推进SCN系列:使用oradebug在mount状态下推进SCN
环境:RHEL 6.5(x86-64) + Oracle 11.2.0.4 声明:推进SCN属于非常规恢复范畴,不建议非专业人员操作,否则后果自负. 需求:我这里演示下推进SCN 10W数量级,实际需 ...
- 总结docker常用命令
docker 1docker pull 镜像 2docker ps -a 查看所有容器docker image 查看镜像 3docker rm 容器id 删除容器 docker rm 一次可以指定多个 ...