前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python和Scala里的闭包

Python和Scala里的闭包

作者头像
哒呵呵
发布2018-08-06 14:14:34
8360
发布2018-08-06 14:14:34
举报
文章被收录于专栏:鸿的学习笔记

在函数式编程里,闭包(closure)是绕不过的话题,它的实现基础来源于变量作用域和一等函数。也正是因为如此,我们可以进一步把代码块抽象,Python也诞生了装饰器。

1.闭包的概念: 闭包源于λ表达式,它的概念核心分为两块,1.上下文环境 2.控制流程。进一步地说,闭包是绑定了自由变量的函数实例。通常来讲,闭包地实现机制是定义一个特殊的数据结构,保存了函数地址指针与闭包创建时的函数的词法环境以及绑定自由变量。对于闭包最好的解释,莫过于《流程的Python》里给出的“它是延伸了作用域的函数,其中包括函数定义体引用,但是不在定义体定义的非全局变量。核心在于闭包能够访问定义体之外定义的非全局变量。”

2.闭包的具体实现: 首先我们需要区分什么是自由变量和绑定变量。自由变量指的是函数自身没有提供这个参数,而绑定变量则是它在函数上下文有着明确的含义。例如: Scala

代码语言:javascript
复制
scala> val add = (x:Int) => x + more
<console>:7: error: not found: value more
       val add = (x:Int) => x + more

scala> var more = 1
more: Int = 1

scala> val add = (x:Int) => x + more
add: Int => Int = <function1>

scala> add(2)
res1: Int = 3

Python

代码语言:javascript
复制
def increase(more):
    return lambda x : x + more

inc1 = increase(1)
inc9 = increase(9)

inc1(10)
Out[3]: 11

inc9(10)
Out[4]: 19

我们可以注意到这里的more就是自由变量,x是匿名函数的绑定变量。x很明确的是由匿名函数定义了,more并没有绑定在匿名函数里面。在Scala里,匿名函数本身没有给予more任何含义,但是只要预先定义了more变量,则add函数可以正常运行了。而Python因为是在运行期才会检查more是否存在,所以函数可以正常的定义,而在后面我们赋值给了more,increase函数便正常运行了。

此时的add/increase函数被称为闭包,它“捕获”自身的自由变量从而“闭合”该匿名函数。接下来看看Scala和Python有什么特别之处吧!

Scala: 在Scala里“捕获”的是变量本身,而不是变量本身引用的值。

代码语言:javascript
复制
scala> more = 100
more: Int = 100

scala> add(2)
res2: Int = 102

当然,反过来也是成立的,闭包也可以修改其自由变量

代码语言:javascript
复制
scala> val minusOne = (x:Int) => {more = more - x}
minusOne: Int => Unit = <function1>

scala>  minusOne(1)

scala> more
res4: Int = 99

那么问题来了,如果more这个变量随着程序的运行被修改了很多次,那么闭包会选择哪一个呢?Scala的答案是,闭包被创建时这个变量最新的那个。(根据定义函数的词法作用域计算自由变量)

代码语言:javascript
复制
scala> def Increase(more:Int) = (x:Int) => x + more
Increase: (more: Int)Int => Int

scala> val Inc1 = Increase(1)
Inc1: Int => Int = <function1>

scala> val Inc9 = Increase(9)
Inc9: Int => Int = <function1>

scala> Inc1(10)
res5: Int = 11

scala> Inc9(10)
res6: Int = 19

Python:

因为Python的函数本身是对象,它提供了很多方法查看函数的参数,例如我们可以查看上面例子的变量:

代码语言:javascript
复制
inc1.__code__.co_varnames
Out[6]: ('x',)

inc1.__code__.co_freevars
Out[7]: ('more',)

在__code__里可以清楚的看到more是自由变量,而x是绑定变量。而more绑定在__closure__方法里。

代码语言:javascript
复制
inc1.__closure__[0].cell_contents
Out[10]: 1

Python在每一次创建闭包时,自由变量都会重新创建,而不会保存,在Python3引入了nonlocal声明解决这个问题。在Python里,由闭包引申了装饰器的概念。这是因为装饰器依赖于内部函数的嵌套。例如:

代码语言:javascript
复制
def deco(func):
    def inner():
        print("it's inner")
    return inner

@deco    
def target():
    print("it's target")

target()
it's inner

这里的target会作为参数传入deco,而deco返回的是inner函数。在name里,target这个名字并不会存在了,取而代之的是inner函数。

代码语言:javascript
复制
a = target
a.__name__
Out[18]: 'inner'

一个小的知识点,装饰器在导入模块时会立即运行,而被装饰的函数则会在明确调用时才会运行。

代码语言:javascript
复制
def deco(func):
    print("runing deco")
    def inner():
        print("it's inner")
    return inner

@deco    
def target():
    print("it's target")

runing deco
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档