关于python中的闭包

参考:

概念

  • 闭包(closure),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
  • 在python中直观的定义就是,一个内部定义的函数引用外部函数的变量(非全局),并且外部函数返回内部函数
  • 一般来说程序被加载到内存,函数定义的代码会放在代码段中,函数被调用时会在栈上创建其运行环境,初始化其内部定义的变量,传入形参,运行结束后,栈空间被回收,其临时变量和运行结果不会被保留。

示例

def bookshop():
    books = []

    def inner(name):
        books.append(name)
        print(books)
    return inner


shop1 = bookshop()
shop1("java")
shop1("python")
shop1("c")
shop2 = bookshop()
shop2("GO")
shop1("c++")
shop2("ruby")

输出

['java']
['java', 'python']
['java', 'python', 'c']
['GO']
['java', 'python', 'c', 'c++']
['GO', 'ruby']
  • 内部函数inner()引用外部books变量,外部函数bookshop返回内部函数,其中这里的books称为自由变量free variable
  • 闭包中的引用的自由变量只和具体的闭包有关联,闭包的每个实例引用的自由变量互不干扰
  • 一个闭包实例对其自由变量的修改会被传递到下一次该闭包实例的调用

闭包的特点和应用

  • 闭包中的自由变量可以被记录下来,下次引用该闭包实例的时候依旧可以使用,所以最常见的用法是实现单例模式
  • 闭包会破坏原来的定义,导致原来对类的引用不兼容,在单例模式的示例中可以看到,被闭包装饰器修饰的类的类型已经不再是一个类而是一个函数了

解释闭包

Closure cells refer to values needed by the function but are taken from the surrounding scope.

闭包的cells指的是被(内嵌)函数所需要的值,但是从该函数的作用域取值。

When Python compiles a nested function, it notes any variables that it references but are only defined in a parent function (not globals) in the code objects for both the nested function and the parent scope. These are the co_freevars and co_cellvars attributes on the __code__ objects of these functions, respectively.

当python的编译器编译一个内嵌函数的时候,只会关注在内嵌函数和父函数作用域中定义的变量(全局变量除外),这就是函数属性__code__中的co_freevarsco_cellvars属性值

Then, when you actually create the nested function (which happens when the parent function is executed), those references are then used to attach a closure to the nested function.

当真正创建内嵌函数(父函数执行的时候),这些引用将被关联到内嵌函数的闭包中去

A function closure holds a tuple of cells, one each for each free variable (named in co_freevars); cells are special references to local variables of a parent scope, that follow the values those local variables point to.

一个函数闭包包含一个cells的元组,每个自由变量对应元组的每一项,cells是引用父函数作用域 local variables的特殊引用,值和local variables一致

引申

nonlocal、global和作用域scope

code block:作为一个单元(Unit)被执行的一段python程序文本。例如一个模块、函数体和类的定义等。

scope:在一个code block中定义name的可见性;

block’s environment:对于一个code block,其所有scope中可见的name的集合构成block的环境。

bind name:下面的操作均可视为绑定操作

函数的形参
import声明
类和函数的定义
赋值操作
for循环首标
异常捕获中相关的赋值变量

local variable:如果name在一个block中被绑定,该变量便是该block的一个local variable。

global variable:如果name在一个module中被绑定,该变量便称为一个global variable。

free variable: 如果一个name在一个block中被引用,但没有在该代码块中被定义,那么便称为该变量为一个free variable。

def fun():
    name = "world"

    def inner():
        print("hello, {}".format(name))
    inner()

fun()
  • 上述的name就是local variabel,在内部函数中对local variable引用是没有问题的,但是如果要对其进行修改,比如下面这样,这里就会报UnboundLocalError: local variable 'name' referenced before assignment,因为在inner函数里面 name += " cup"是对name进行赋值,那name就被当做inner的一个local variable去寻找初始值,因此会报UnboundLocalError错误
def fun():
    name = "world"

    def inner():
        name += " cup"
        print("hello, {}".format(name))
    inner()

fun()
  • 怎么解决这个问题,加上nolocal关键字即可,同样道理和操作适用于global
def fun():
    name = "world"

    def inner():
        nonlocal name
        name += " cup"
        print("hello, {}".format(name))
    inner()

fun()

函数作用域的LEGB顺序

python在函数里面的查找分为4种,称之为LEGB,也正是按照这是顺序来查找的

  • L: local 函数内部作用域
  • E: enclosure 函数内部与内嵌函数之间
  • G: global 全局作用域
  • B: build-in 内置作用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值