写在前面
其实闭包这个词在很多的地方都听到过,以前看到说这是前端javascript这门语言的所具有的特点。当时不知所以然也没接触到对应的概念,现在遇到一个问题就是,为什么c++不支持函数的嵌套定义?对比python,在python当中可以在函数体的内部定义别的函数,这项技术(或者成为语法支持)为什么c++里没有,这样的做法有什么作用呢?
如果你刚好对这方面有和我一样的疑问,那么请往下看。
主要内容
什么是函数的嵌套定义?
Python中一个与众不同的语法就是可以嵌套函数,所谓嵌套,并不像其他语言中的在一个函数中调用另一个函数,而是在定义一个函数的时候,函数体里还能定义另一个函数。 内函数可以访问外函数的作用域。
python当中的函数:
- 函数是第一类对象,即表示函数可以当做数据传递
- 可以被引用:把函数内存地址赋值给一个变量名,仍然遵循函数的调用规则。
- 可以被当做参数传递:传递的是函数的运行的结果#可以当做返回值 把函数作为返回值返回的话,返回的结果是函数的内存地址。
- 可以当做容器类型的元素:意思就是可以作为列表和字典的元素利用函数的特性,可以取代多分枝的if。
c++当中的函数:
是不能够嵌套定义函数的,函数名就是函数的地址,可以定义函数指针,通过函数指针可以像函数名一样调用函数,但是函数指针的类别定义必须符合函数的返回值和参数类型要求(很麻烦)。
通过函数指针,函数是可以当做数据传递的。
函数指针就是函数的引用,可以遵循函数的调用规则。
函数指针也可以当做容器类型的元素。
简单来说,c++的函数指针就可以达到python当中函数的一切要求,我个人觉得如果将函数的范围扩展,c++当中的仿函数(重载了()操作符的class)就完全满足上面的所有的要求。
问题:为什么c++不支持函数的嵌套定义?
参考回答:
语言遇到一个问题时,就会引入一种机制解决它,比如为了实现动多态引入虚函数,为了避免参数被意外的修改而引入 const,所以,在问 C++ 的函数不能嵌套定义之前,先思考一下:
为什么需要嵌套定义函数?
嵌套定义函数是为了解决什么样的问题?
C++语言现有机制解决不了这个问题吗?
我的理解:
c++和python等这类支持嵌套函数定义的语言有一个本质的区别,就是c++是在支持面向对象的情况下还是尽量的为了不损失性能,可以说是就是为了性能而不是为了简洁为了方便而考虑。这是c++和python在本质上的一个区别。所以python语言本身就支持很多高级的东西,无论是支持高级的数据结构还是支持闭包,python语言将很多的信息很多的处理交给了语言本身的设计者交给了解释器去完成,这之间的效率必然是有很大的下降的。
函数的嵌套定义的作用?
参考回答:
当函数实现的功能比较复杂,代码量比较大时,可以对函数进行分片,在这个函数的内部定义实现某一功能的函数,这样做的好处是代码易懂,并且可以对一部分变量封装,避免对变量的误操作。
书上说“存储子封闭作用域的行为叫做闭包”。
闭包是啥?
- 以下是网上各种资料里总结的概念,拿来参考
闭包:closure
闭包就是能够读取其他函数内部变量的函数。闭包包含自由(未绑定到特定对象)变量,这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
应用领域:延迟计算
我个人的理解就是:
闭包就是一种嵌套定义函数和嵌套调用函数的结合的一种组合功能,他是由语言本身语法上的支持所带来的。当前的函数不仅仅是一个单一的函数,这个函数还带有额外的信息,这个信息也不是随便决定的信息,这个信息是带有作用域关系的信息(包含变量的访问权限),这个信息是在这个函数定义的时候所处的位置决定的而不是这个函数被调用的位置决定的。
一个函数定义的地方当然只有一处(否则不就重定义了嘛)。但是一个函数可以在不同的地方被以多种不同的形式被调用。
一个不支持闭包的语言(以c++举例吧~)原生函数就是函数本身,一个函数就是一段代码,这个函数的代码在内存的某个地方当函数被定义的时候,函数名就是这个函数所在位置的入口(内存地址),函数只能够从形参当中获取外界的输入从而和外界进行沟通,这样的函数所包含的信息是固定的,你有多少个参数就有多少个信息,当然还有的就是对全局变量的访问(在c++当中并不提倡使用全部变量)。所以c++当中的函数本身就是提供一个解决某项基本问题的解决办法的算法的集合,达到一处定义多处复用的效果而已。但是python等支持闭包的语言就不是这样,这里的函数还包含了上层作用域的信息,无论函数在什么时间什么地点被怎样的调用,对该函数在定义的时候绑定的上层作用域这个函数都有访问权限,也就是说你可以在另一个作用域的内部访问到不属于该作用域也不属于该作用域能访问到的作用域里的信息!
- 之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路。
- 闭包就是能够读取其他函数内部变量的函数。
闭包的优缺点
闭包可以用在许多地方:
- 一个是前面提到的可以读取函数内部的变量。
- 另一个就是让这些变量的值始终保持在内存中,不会在调用后被自动清除。
- 闭包也具有提高代码可复用性的作用,利用闭包,我们实际上创建了泛函
- 更好的支持并行计算
- 另一种形式的面向对象,在其他编程语言入比如javaScript中,经常用闭包来实现面向对象编程
def func1():
x = 10
def func2():
print(x)
return func2
f = func1()
f()
举例来说:
在调用完f函数unc1之后,func1内部的局部变量x并不会自动的清除,因为func2当中还持有对x的引用。
为什么会这样呢?原因就在于func1是func2的父函数,而func2被赋给了一个全局变量f,这导致func2始终在内存中,而func2的存在依赖于func1,因此func1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数。
在python中一切都是对象,包括整型数据1,函数,其实是对象。
- 对于闭包,在外函数outer中 最后return inner,我们在调用外函数 demo = outer() 的时候,outer返回了inner,inner是一个函数的引用,这个引用被存入了demo中。所以接下来我们再进行demo() 的时候,相当于运行了inner函数。
- 同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。
- 按照我们正常的认知,一个函数结束的时候,会把自己的临时变量都释放还给内存,之后变量都不存在了。一般情况下,确实是这样的。但是闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。
- 在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:1 global 声明全局变量 2 全局变量是可变类型数据的时候可以修改
闭包有效的减少了函数所需定义的参数数目。这 对于并行运算来说有重要的意义。在并行运算的环境下,我们可以让每台电脑负责一个函数,然后将一台电脑的输出和下一台电脑的输入串联起来。最终,我们像流 水线一样工作,从串联的电脑集群一端输入数据,从另一端输出数据。这样的情境最适合只有一个参数输入的函数。闭包就可以实现这一目的。
并行运算正称为一个热点。这也是函数式编程又 热起来的一个重要原因。函数式编程早在1950年代就已经存在,但应用并不广泛。然而,我们上面描述的流水线式的工作并行集群过程,正适合函数式编程。由 于函数式编程这一天然优势,越来越多的语言也开始加入对函数式编程范式的支持。
闭包的缺点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包
- 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
python实现闭包的方式:
闭包函数必须有内嵌函数
内嵌函数需要引用该嵌套函数上一级namespace中的变量
闭包函数必须返回内嵌函数
在Python中,函数对象有一个 __closure__属性,我们可以通过这个属性看看闭包的一些细节。
从这里可以看到闭包的原理,当内嵌函数引用了包含它的函数(enclosing function)中的变量后,这些变量会被保存在enclosing function的closure属性中,成为enclosing function本身的一部分;也就是说,这些变量的生命周期会和enclosing function一样。