python变量及其作用域,闭包

时间:2022-01-21 22:39:03

http://blog.csdn.net/pipisorry/article/details/42525939

建议参考[Python核心编程2ed.pdf: 11.8 变量作用域]

Python 标识符与保留字(关键字)

[Python 标识符与保留字(关键字) ]

python全局变量

在python中,True和False是全局变量,因此:

False = True

if False:

print "Hello"

else:

print "World"

>>> Hello

Python中的“真值”

在Python和Django模板系统中,以下这些对象相当于布尔值的False:

  • 空列表([] )

  • 空元组(() )

  • 空字典({} )

  • 空字符串('' )

  • 零值(0, 0.0)

  • 特殊对象None

  • 对象False(很明显)

Note:

1. 你也可以在自定义的对象里定义他们的布尔值属性(python的高级用法)。

2. 除以上几点以外的所有东西都视为`` True``,如-1。

python数字类型处理:最大float、epsilon

[python模块:数字处理模块]

皮皮blog



global 语句

global语句用来声明x是全局变量,当我们在函数内给x赋值时,它的改变映射到我们在主块中使用的x的值。
用同样的global语句可以指定多个全局变量,比如: global x, y, z。

你在函数定义中声明的变量,他们与在函数外使用的其它同名变量没有任何关系,即变量名称对函数来说是局部的。

    x = 50
    def func(x):
        print('x等于', x)
        x = 2
        print('局部变量x改变为', x)

    func(x)
    print('x一直是', x)

输出:
x等于50
局部变量x改变为2
x一直是50

使用全局声明

如果你想给在顶层的程序(即未在任何类型的范围如函数或类之中)定义的变量赋值,那么你必须告诉Python,变量不是局部的,而是全局的。

    x = 50
    def func():
        global x
        print('x的值是', x)
        x = 2
        print('全局变量x改为', x)
    func()
    print('x的值是', x)

输出:
x的值是50
全局变量to改为2
x的值是2

皮皮blog


python闭包

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure。定义在外部函数内的但由内部函数引用或者使用的变量被称为*变量。

闭包将内部函数自己的代码和作用域以及外部函数的作用结合起来。闭包的词法变量不属于全局名字空间域或者局部的--而属于其他的名字空间,带着“流浪"的作用域。(注意这不同于对象因为那些变量是存活在一个对象的名字空间但是闭包变量存活在一个函数的名字空间和作用域)

闭包示例

def addx(x):  
    def adder(y): return x + y  
    return adder  
>>> c =  addx(8)  
>>> type(c)  
    <type 'function'>  
>>> c.__name__  
    'adder'  
>>> c(10)  
    18 
说明闭包:

如果在一个内部函数里:adder(y)就是这个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用:x就是被引用的变量,x在外部作用域addx里面,但不在全局作用域里,则这个内部函数adder就是一个闭包。 再稍微讲究一点的解释是,闭包=函数块+定义函数时的环境,adder就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x。

闭包经典示例1

闭包中是不能修改外部作用域的局部变量的!

def foo():  
    a = 1  
    def bar():  
        a = a + 1  
        return a  
    return bar 
运行出错:UnboundLocalError: local variable 'a' referenced before assignment.

在闭包bar()中,变量a在赋值符号"="的左面,被python认为是bar()中的局部变量。再接下来执行print c()时,程序运行至a = a + 1时,因为先前已经把a归为bar()中的局部变量,所以python会在bar()中去找在赋值语句右面的a的值,结果找不到,就会报错。

解决的方法

方法1:

def foo():  
    a = [1]  
    def bar():  
        a[0] = a[0] + 1  
        return a[0]  
    return bar 

只要将a设定为一个容器就可以了。a[0]只是引用了已定义的a列表,而不是

方法2:

在python3以后,在a = a + 1 之前,使用语句nonlocal a就可以了,该语句显式的指定a不是闭包的局部变量。

def foo():  
    a = 1  
    def bar(): 

        nonlocal a

        a = a + 1  
        return a  
    return bar 

Note: 引用全局的变量a用global, 闭包中使用nonlocal!

闭包经典示例2

在程序里面经常会出现这类的循环语句(range对象在python3中是著名的惰性求值(lazy evaluation)),Python的问题就在于,当循环结束以后,循环体中的临时变量i不会销毁,而是继续存在于执行环境中。还有一个python的现象是,python的函数只有在执行时,才会去找函数体里的变量的值

1>

    flist = []  
    for i in range(3):  
        def foo(x): print x + i  
        flist.append(foo)  
    for f in flist:  
        f(2)

可能有些人认为这段代码的执行结果应该是2,3,4.但是实际的结果是4,4,4。这是因为当把函数加入flist列表里时,python还没有给i赋值,只有当执行时,再去找i的值是什么,这时在第一个for循环结束以后,i的值是2,所以以上代码的执行结果是4,4,4。
2>
li = [lambda :x for x in range(10)]
判断下li的类型?li里面的元素为什么类型?
print(type(li))
print(type(li[0]))
# <class 'list'>
# <class 'function'>
可以看到li为列表类型,list里面的元素为函数,那么打印list里面第一个元素的返回值,此时返回值为多少?
res = li[0]()
print(res)
#输出:9
li第一个函数的返回值为9还不是0,记住:函数在没有执行前,内部代码不执行。
3>

def multipliers():
    return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]

以上代码的输出是 [6, 6, 6, 6] (而不是 [0, 2, 4, 6])。

Note:

1 这里lambda x: i*x因为在其它函数内部,而且还引用了非全局外部变量i,所以形成一个闭包。

2 当循环结束以后,循环体中的临时变量i不会销毁,而是继续存在于执行环境中。

函数调用时就是下面这样,但是此时i的值是使用时才查找,调用时再查找,此时值已为3。

def multipliers():

    return [lambda x : i *x ,lambda x : i *x ,lambda x : i *x ,lambda x : i *x ],且i的值为3保存在当前执行环境中。

即Python 的闭包是后期绑定 late binding,这意味着在闭包中的变量是在内部函数被调用的时候被查找。所以结果是,当任何 multipliers() 返回的函数被调用,在那时,i 的值是在它被调用时的周围作用域中查找,到那时,无论哪个返回的函数被调用,for 循环都已经完成了,i 最后的值是 3,因此,每个返回的函数 multiplies 的值都是 3。因此一个等于 2 的值被传递进以上代码,它们将返回一个值 6 (比如: 3 x 2)。

(顺便说下,正如在 The Hitchhiker’s Guide to Python 中指出的,这里有一点普遍的误解,一个 lambda 表达式创建的函数不是特殊的,和使用一个普通的 def 创建的函数展示的表现是一样的。)

这里有几种方法解决闭包的后期绑定问题

1 最普遍的解决方案是创建一个闭包,通过使用默认参数立即绑定它的参数
def multipliers():    return [lambda x, i=i : i * x for i in range(4)] 

for i in range(3):  
    def foo(x,y=i): print x + y  
    flist.append(foo)
2 使用 functools.partial 偏函数
from functools import partialfrom operator import muldef multipliers():    return [partial(mul, i) for i in range(4)]
3 使用生成器语法
def multipliers():    for i in range(4):        yield lambda x: i * xprint [m(2) for m in multipliers()]

yield内部是一个状态机,维护着挂起和继续的状态。这样可以保持i的值。[python迭代器、生成器和yield语句]

那么,为什么你会想要用 closues?

Closurs 对于安装计算,隐藏状态,以及在函数对象和作用域中随意地切换是很有用的。closurs在 GUI 或者在很多 API 支持回调函数的事件驱动编程中是很有些用处的。以绝对相同的方式,应用于获取数据库行和处理数据。回调就是函数。闭包也是函数,但是他们能携带一些额外的作用域。它们仅仅是带了额外特征的函数......另外的作用域。
你可能会觉得闭包的使用和这章先前介绍的偏函数应用非常的相似,但是与闭包的使用相比,PFA 更像是 currying, 因为闭包和函数调用没多少相关,而是关于使用定义在其他作用域的变量。

这个闭包在实际的开发中有什么用呢?闭包主要是在函数式开发过程中使用。以下介绍两种闭包主要的用途。


用途1,当闭包执行完后,仍然能够保持住当前的运行环境。 比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向(direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。    origin = [0, 0]  # 坐标系统原点  
    legal_x = [0, 50]  # x轴方向的合法坐标  
    legal_y = [0, 50]  # y轴方向的合法坐标  
    def create(pos=origin):  
        def player(direction,step):  
            # 这里应该首先判断参数direction,step的合法性,比如direction不能斜着走,step不能为负等  
            # 然后还要对新生成的x,y坐标的合法性进行判断处理,这里主要是想介绍闭包,就不详细写了。  
            new_x = pos[0] + direction[0]*step  
            new_y = pos[1] + direction[1]*step  
            pos[0] = new_x  
            pos[1] = new_y  
            #注意!此处不能写成 pos = [new_x, new_y],原因在上文有说过  
            return pos  
        return player  
      
    player = create()  # 创建棋子player,起点为原点  
    print player([1,0],10)  # 向x轴正方向移动10步  
    print player([0,1],20)  # 向y轴正方向移动20步  
    print player([-1,0],10)  # 向x轴负方向移动10步 

输出为

    [10, 0]  
    [10, 20]  
    [0, 20] 

用途2,闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。

    def make_filter(keep):  
        def the_filter(file_name):  
            file = open(file_name)  
            lines = file.readlines()  
            file.close()  
            filter_doc = [i for i in lines if keep in i]  
            return filter_doc  
        return the_filter 

如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序
    filter = make_filter("pass")  
    filter_result = filter("result.txt")

以上两种使用场景,用面向对象也是可以很简单的实现的,但是在用Python进行函数式编程时,闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的。

皮皮blog



python变量使用域

在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域;在Python 2.2中,Python正式引入了一种新的作用域 --- 嵌套作用域;在Python 2.1中,嵌套作用域可以作为一个选项被开启;嵌套作用域的引入,本质上为Python实现了对闭包的支持,关于闭包的知识,网上有很多解释,这里就不详细展开了。相应地,变量查找顺序由之前的LGB变成LEGB(L:Local,E:Enclosing,G:Global,B:Built-in)

在Python中,名字绑定在所属作用域中引入新的变量,同时绑定到一个对象。名字绑定发生在以下几种情况之下:

    1.参数声明:参数声明在函数的局部作用域中引入新的变量;

    2.赋值操作:对一个变量进行初次赋值会在当前作用域中引入新的变量,后续赋值操作则会重新绑定该变量;

    3.类和函数定义:类和函数定义将类名和函数名作为变量引入当前作用域,类体和函数体将形成另外一个作用域;

    4.import语句:import语句在当前作用域中引入新的变量,一般是在全局作用域;

    5.for语句:for语句在当前作用域中引入新的变量(循环变量);

    6.except语句:except语句在当前作用域中引入新的变量(异常对象)。

    在Python中,类定义所引入的作用域对于成员函数是不可见的,这与C++或者Java是很不同的,因此在Python中,成员函数想要引用类体定义的变量,必须通过self或者类名来引用它。

    嵌套作用域的加入,会导致一些代码编译不过或者得到不同的运行结果,在这里Python解释器会帮助你识别这些可能引起问题的地方,给出警告。

    locals函数返回所有的局部变量,但是不会返回嵌套作用域中的变量,实际上没有函数会返回嵌套作用域中的变量。

[Python的作用域]

python能够改变变量作用域的代码段是def、class、lamda.

if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的

变量搜索路径是:本地变量->全局变量

python能够改变变量作用域的代码段是def、class、lamda.

def scopetest():  
    localvar=6;  
    print(localvar)  
    
scopetest()  
#print(localvar) #去除注释这里会报错,因为localvar是本地变量
if/elif/else、try/except/finally、for/while
    while True:  
        newvar=8  
        print(newvar)  
        break;  
      
    print(newvar)  
      
    try:  
        newlocal=7  
        raise Exception  
    except:  
        print(newlocal)#可以直接使用哦  
输出结果:8 8 7

可见这个关键字中定义变量,他们的作用域跟外部是一致的,这个跟Java的作用域概念有点不一样。

Note: 所以在函数外如果定义了一个全局变量a,再在函数中对变量a += 1赋值就会报错NameError: name 'a' is not defined,变量未声明就使用。

皮皮blog



python作用域

1、没有块级作用域

想想此时运行下面的程序会有输出吗?执行会成功吗?
#块级作用域
if 1 == 1:
    name = "lzl"
print(name)
for i in range(10):
    age = i
print(age)
执行结果
lzl
代码执行成功,没有问题;在Java/C#中,执行上面的代码会提示name,age没有定义,而在Python中可以执行成功,这是因为在Python中是没有块级作用域的,代码块里的变量,外部可以调用,所以可运行成功。

2、局部作用域

函数是个单独的作用域,Python中没有块级作用域,但是有局部作用域
#局部作用域
def  func():
    name = "lzl"
print(name)
运行这段代码,NameError: name 'name' is not defined,运行报错,name变量只在func()函数内部中生效,所以在全局中是没法调用的。

对上面代码做个简单调整,再看看结果如何?
#局部作用域
def  func():
    name = "lzl"
func()          #执行函数
print(name)
对之前的代码添加了一句代码,在变量name打印之前,执行了一下函数,NameError: name 'name' is not defined,执行依然报错:即使执行了一下函数,name的作用域也只是在函数内部,外部依然无法进行调用。

3、作用域链

对函数做下调整
#作用域链
name = "lzl"
def f1():
    name = "Eric"
    def f2():
        name = "Snor"
        print(name)
    f2()
f1()
最后f1()执行完会输出Snor;Python中有作用域链,变量会由内到外找,先去自己作用域去找,自己没有再去上级去找,直到找不到报错。

4、终极版作用域

#终极版作用域
name = "lzl"
def f1():
    print(name)
def f2():
    name = "eric"
    f1()
f2()
想想最后f2()执行结果是打印“lzl”呢,还是打印“eric”?先看看下面这段代码:
#终极版作用域
name = "lzl"
def f1():
    print(name)
def f2():
    name = "eric"
    return f1
ret = f2()
ret()
#输出:lzl
执行结果为“lzl”,分析下上面的代码,f2()执行结果为函数f1的内存地址,即ret=f1;执行ret()等同于执行f1(),执行f1()时与f2()没有任何关系,name=“lzl”与f1()在一个作用域链,函数内部没有变量是会向外找,所以此时变量name值为“lzl”;理解了这个,那么刚才没给出答案的那个终极代码你也知道答案了
所以前面的输出的也是“lzl”,记住在函数未执行之前,作用域已经形成了,作用域链也生成了

from:http://blog.csdn.net/pipisorry/article/details/42525939

ref: