参考:
概念
- 闭包(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
andco_cellvars
attributes on the__code__
objects of these functions, respectively.当python的编译器编译一个内嵌函数的时候,只会关注在内嵌函数和父函数作用域中定义的变量(全局变量除外),这就是函数属性
__code__
中的co_freevars
和co_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 内置作用