埃尔法哥哥:Python——五分钟理解函数式编程与闭包( 二 )


这么说有点拗口 , 我们来看一个简单的例子 。 在Python当中有一个函数叫做math.pow其实就是计算次方的 。 比如我们要计算x的平方 , 那么我们应该这样写:
math.pow(x,2)
但是如果我们当前场景下只需要计算平方 , 我们每次都要传入额外再传入一个2会显得非常麻烦 , 这个时候我们使用闭包 , 可以简化操作:
埃尔法哥哥:Python——五分钟理解函数式编程与闭包
文章图片
通过闭包 , 我们把第二个变量给固定了 , 这样我们只需要使用pow2就可以实现原来math.pow(x,2)的功能了 。 如果我们突然需求变更需要计算3次方或者是4次方 , 我们只需要修改mypow的传入参数即可 , 完全不需要修改代码 。
实际上这也是闭包最大的使用场景 , 我们可以通过闭包实现一些非常灵活的功能 , 以及通过配置修改一些功能等操作 , 而不再需要通过代码写死 。 要知道对于工业领域来说 , 线上的代码是不能随便变更的 , 尤其是客户端 , 比如applestore或者是安卓商店当中的软件包 , 只有用户手动更新才会拉取 。 如果出现问题了 , 几乎没有办法修改 , 只能等用户手动更新 。 所以常规操作就是使用一些类似闭包的灵活功能 , 通过修改配置的方式改变代码的逻辑 。
除此之外闭包还有一个用处是可以暂存变量或者是运行时的环境 。
举个例子 , 我们来看下面这段代码:
埃尔法哥哥:Python——五分钟理解函数式编程与闭包
文章图片
这是没有使用闭包的函数 , 不管我们调用多少次 , 答案都是5 , 执行完x+=5之后的结果并不会被保存起来 , 当函数返回了 , 这个暂存的值也就被抛弃了 。 那如果我希望每次调用都是依据上次调用的结果 , 也就是说我们每次修改的操作都能保存起来 , 而不是丢弃呢?
这个时候就需要使用闭包了:
埃尔法哥哥:Python——五分钟理解函数式编程与闭包
文章图片
也就是说我们的x的值被存储起来了 , 每次修改都会累计 , 而不是丢弃 。 这里需要注意一点 , 我们用到了一个新的关键字叫做nonlocal , 这是Python3当中独有的关键字 , 用来申明当前的变量x不是局部变量 , 这样Python解释器就会去全局变量当中去寻找这个x , 这样就能关联上test方法当中传入的参数x 。 Python2官方已经不更新了 , 不推荐使用 。
由于在Python当中也是一切都是对象 , 如果我们把闭包外层的函数看成是一个类的话 , 其实闭包和类区别就不大了 , 我们甚至可以给闭包返回的函数关联函数 , 这样几乎就是一个对象了 。 来看一个例子:
埃尔法哥哥:Python——五分钟理解函数式编程与闭包
文章图片
最后运算的结果是xiaohong , 因为我们调用set_name改变了闭包外部的值 。 这样当然是可以的 , 但是一般情况下我们并不会用到它 。 和写一个class相比 , 通过闭包的方法运算速度会更快 。 原因比较隐蔽 , 是因为闭包当中没有self指针 , 从而节省了大量的变量的访问和运算 , 所以计算的速度要快上一些 。 但是闭包搞出来的伪对象是不能使用继承、派生等方法的 , 而且和正常的用法格格不入 , 所以我们知道有这样的方法就可以了 , 现实中并不会用到 。
闭包的坑
闭包虽然好用 , 但是不小心的话也是很容易踩坑的 , 下面介绍几个常见的坑点 。
闭包不能直接访问外部变量
这一点我们刚才已经提到了 , 在闭包当中我们不能直接访问外部的变量的 , 必须要通过nonlocal关键字进行标注 , 否则的话是会报错的 。
埃尔法哥哥:Python——五分钟理解函数式编程与闭包
文章图片
比如这样的话 , 就会报错:
闭包当中不能使用循环变量


推荐阅读