命名空间、作用域与闭包

Posted by Waynerv on

category: default

Tags: Python

1 命名空间和作用域

1.1 命名空间 namespace

命令空间的定义:从名字到对象的一个映射(a mapping from name to objects)

参考Python文档的说法,大部分namespace都是按python中的字典来实现的,但将来可能发生改变。

一些常见的namespace:built-in 内置函数,内置异常,一个模块中的全局变量,一个函数调用时的局部变量。

在某种意义上,一个对象 (object) 的所有属性 (attribute) 也构成了一个 namespace。 在程序执行期间,肯定会有多个命名空间同时存在。

不同namespace的创建/销毁时间也不同。包含内置名称(内置函数等)的命名空间在Python解释器启动时创建,且不会删除。函数的局部命名空间在函数被调用时创建,在函数返回或产生非函数内部可处理的异常时删除。

此外,两个不同 namespace 中的两个相同名字的变量之间没有任何联系。

1.2 作用域 scope

A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

Scope是Python程序的一块文本区域(textual region)。在该文本区域中,对 namespace 可以直接访问,而不需要通过属性来访问。

Scope是定义程序该如何搜索确切地“名字-对象”的命名空间的层级关系。

  • 直接访问:对一个变量名的引用会在所有namespace中查找该变量,而不是通过属性访问。
  • 属性访问:所有名字后加 . 的都认为是属性访问。

module_name.func_name ,需要指定 func_name 的名空间,属于属性访问。 而abs(-1)abs 属于直接访问。

1.3 两者联系

在Python中,scope是由namespace按特定的层级结构组合起来的

scope一定是namespace,但namespace不一定是scope.

2 LEGB 规则

在一个Python程序运行中,至少有4个scopes是存在的。 直接访问一个变量可能在这四个namespace中按特定顺序逐一搜索。

  • Local(innermost)

    包含局部变量。 比如一个函数/方法内部定义的变量。

  • Enclosing

    包含了非局部(non-local)也非全局(non-global)的变量。 比如两个嵌套函数,内层函数可能搜索外层函数的namespace,但该namespace对内层函数而言既非局部也非全局。

  • Global(next-to-last)

    当前脚本的最外层。 比如当前模块的全局变量。

  • Built-in(outtermost)

    Python __builtin__ 模块。 包含了内建的变量/关键字等。

Python搜索对应作用域的顺序为著名的”LEGB-rule”,即scope的搜索顺序为:Local -> Enclosing -> Global -> Built-in 。

3 赋值规则

赋值语句通常隐式地会创建一个局部(local)变量,即便该变量名已存在于赋值语句发生的上一层作用域中;

如果没有 global 关键字声明变量,对一个变量的赋值总是认为该变量存在于最内层(innermost)的作用域中;

示例:

def outer():
    a = 0
    b = 1
    def inner():
        print a
        print b
        # b += 1        # A
        b = 4           # B
    inner()

outer()

运行结果中将报错:

UnboundLocalError: local variable 'b' referenced before assignment

4 for循环

for循环中根据迭代的对象对target list中的变量进行赋值,这会覆盖掉所有之前对这些变量的赋值,包括在循环体内进行的赋值。

for 后面跟着的变量(target list)在循环结束后不会被删除,但如果 for 循环的序列为空,这些变量不会被赋值。

5 列表推导式与生成器表达式

列表推导式和生成器表达式会引入新的作用域(Python3特性)。

6 闭包

如果函数体内引用了更高一级的变量x,x在函数被定义环境的命名空间内,我们称x为函数的环境变量。

一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的__closure__属性中。

__closure__里包含了一个元组(tuple)。这个元组中的每个元素是cell类型的对象。cell中会包含我们创建闭包时的环境变量的取值。

6.1 创建闭包

在Python中创建一个闭包可以归结为以下三点:

  • 闭包函数必须有内嵌函数
  • 内嵌函数需要引用该嵌套函数上一级namespace中的变量
  • 闭包函数必须返回内嵌函数

注:转载本文,请与作者联系




如果觉得文章对您有价值,请作者喝杯咖啡吧

|
donate qrcode

欢迎通过微信与我联系

wechat qrcode

0 Comments latest

No comments.