Skip to content
cym edited this page Jan 12, 2018 · 1 revision

Python 笔记

Python 的作用域问题

变量的作用域

Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域Python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在_源代码中的位置_决定。

高级语言对数据类型的使用过程

一般的高级语言在使用变量时,都会有下面4个过程。当然在不同的语言中也会有着区别。

  1. 声明变量:让编辑器知道有这一个变量的存在
  2. 定义变量:为不同数据类型的变量分配内存空间
  3. 初始化:赋值,填充分配好的内存空间
  4. 引用:通过引用对象(变量名)来调用内存对象(内存数据)

Python 的作用域;LEGB法则

Python没有_块作用域_(if-elif-elsefor-elsewhiletry-except\try-finally等关键字的语句块),只有当变量在Module(模块)Class(类)def(函数)中定义的时候,才会有作用域的概念

Python中,使用一个变量时并不严格要求需要预先声明它,但是在真正使用它之前,它必须被绑定到某个内存对象(被定义、赋值);这种变量名的绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量。

LEGB法则:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域

globalVar = 100           #G(global)全局作用域

def test_scope():
    enclosingVar = 200    #E(enclosing)嵌套作用域
    def func():
        localVar = 300    #L(local)局部作用域
print __name__            #B(built-in)内置作用域

一个典型例子

variable = 300
def test_scopt():
    print(variable)   #variable是test_scopt()的局部变量,但是在打印时并没有绑定内存对象。
    variable = 200

test_scopt()
print(variable)

代码输出为:

UnboundLocalError: local variable 'variable' referenced before assignment

上面的例子会报出错误,因为在执行程序时的__预编译__能够在test_scopt()中找到局部变量variable(对variable进行了赋值)。在局部作用域找到了变量名,所以__不会__升级到嵌套作用域去寻找。但是在使用print语句将变量variable打印时,局部变量variable并没有没绑定到一个内存对象(没有定义和初始化,即没有赋值)。本质上还是Python调用变量时遵循的LEGB法则Python解析器的编译原理,决定了这个错误的发生。所以,在调用一个变量之前,需要为该变量赋值(绑定一个内存对象)。

注意:为什么在这个例子中触发的错误是UnboundLocalError而不是NameError:name ‘variable’ is not defined。因为变量variable不在全局作用域。Python中的_模块代码_在执行之前,并不会经过预编译,但是模块内的_函数体代码_在运行前会经过预编译,因此不管变量名的绑定发生在作用域的那个位置,都能被编译器知道。Python虽然是一个静态作用域语言,但变量名查找是动态发生的,直到在程序运行时,才会发现作用域方面的问题。

类比 JavaScript 变量提升

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

console.log(a);//undefined
var a = 10;
console.log(a);//10

等价于

var a ;	//声明变量
console.log(a);
a = 10;	//初始化
console.log(a);

修改变量作用域

  1. L中修改G中的变量,使用global关键字

    spam = 99
    def tester():
        def nested():
            global spam
            print('current=',spam)
            spam = 200
        return nested
    tester()()
    print spam

    输出为:

    ('current=', 99)
    200
    
  2. L中修改E中的变量,使用nonlocal关键字

    def outer():
        count = 10
        def inner():
            nonlocal count
            count = 20
            print(count)
        inner()
        print(count)
    outer()

    输出为:

    20
    20
    

闭包

Python支持一种特性叫做函数闭包(function closures):在非全局(global)作用域中定义inner函数(即嵌套函数)时,会记录下它的嵌套函数namespaces(嵌套函数作用域的locals),可以称作:定义时状态,可以通过func_closure这个属性来获得inner函数的外层嵌套函数的namespaces

例子:

def outer(x):  
    def inner():  
        print x # 1  
    return inner  
print1 = outer(1)  
print2 = outer(2)  
print print1.func_closure  
print1()  
print print2.func_closure  
print2()

输出为:

(<cell at 0x147d3328: int object at 0x146b2d08>,) #这里面里面保存了一个int对象,这个int对象就是x
1
(<cell at 0x147d3360: int object at 0x146b2cf0>,)
2

参考

Python变量作用域及闭包

JavaScript 变量提升

Clone this wiki locally