python 函数作用域

变量可以在三个不同的地方分配
1. 如果一个变量在def内赋值,它被定位在这个函数之内
2. 如果一个变量在一个嵌套的def中赋值,对于嵌套函数来说,他是非本地的
3. 如果在def之外赋值,他就是整个文件全局的

作用域法则
1. 内嵌模块是全局作用域
2. 全局作用域的作用范围仅限于单个文件
3. 每次对函数的调用都创建了一个新的本地作用域
4. 函数中赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量
5. 所有其他的变量名都可以归纳为本地,全局或者内置的

函数内部任何赋值都会把一个名称划分为本地的,包括:=语句,import中的模块名称,def中的函数名称,函数参数名称等
函数内部改变对象并不会把变量划分为本地,如果修改全局列表L,L.append('a')

变量名解析
1. 变量名引用分为三个作用域进行查询,首先是本地,然后是上层函数的本地作用域,之后全局,最后是内置
2. 默认情况下,变量名赋值会创建或者改变本地变量
3. 全局声明和非本地声明将赋值的变量名映射到模块文件内部的作用域

内置作用域

内置作用域是一个名为builtin的内置模块,但是必须要import之后才能使用内置作用域,legb法则python会自动搜索该模块,因此不需要导入也可以使用内置变量

global语句告诉python函数打算生成一个或多个全局变量名
全局变量位于模块文件内部顶层的变量名
全局变量如果是在函数内被赋值的话,必须经过声明
全局变量在函数的内部不经过声明也可以被引用

x=88
def func():
    global x    #声明x引用def之外的x
    x=99
func()
print(x)    #99

x,y=1,2
def all_global():
    global x    #声明x引用def之外的x,y也是全局变量
    x=y+2
all_global()
print(x)    #4

#嵌套函数,x的作用域为当前函数和所有嵌套函数
x=99
def f1():
    x=88
    def f2():
        print(x)
        def f3():
            print(x)
        f3()
    f2()
    
f1()

#工厂函数,一个能够记住嵌套作用域的变量值的函数,尽管那个作用域或许已经不存在
def f3():
    x=88
    def f4():
        print(x)
    return f4

action=f3()
action()  #结果:88,f4只存在与f3中,此时f4已经不处于激活状态,但是依旧记住了x的值

def maker(n):
    def action(x):
        return x**n
    return action

f=maker(2)
print(f(3))   #函数action记住了嵌套作用域变量n,因此3**2=9
g=maker(3)
print(g(3))    #得到一个新的n

#使用默认参数来保留嵌套作用域的状态

def f5():
    x=88
    def f6(y=x):   #默认参数记住了f5中的x值
        print(y)
    return f6

f=f5()
f()

#避免使用def嵌套,等价函数如下
def f7():
    x=88
    f8(x)
    
def f8(x):
    print(x)
    
f7()

#嵌套作用域和lambda
def func():
    x=4
    action=(lambda n:x**n)   #lambda可使用嵌套函数变量
    return action

action=func()
print(action(2))

def func2():
    x=4
    action=(lambda n, m=x:m**n)   #lambda中使用默认函数来记住嵌套函数变量
    return action

action=func2()
print(action(2))

#作用域与带有循环变量的默认参数相比较
#嵌套函数,嵌套在一个循环之中,并且嵌套函数引用上层作用域的变量,该变量被循环改变,所有在这个循环中产生的函数将会有相同的值,在最后一次循环中完成时被引用变量的值

def makeActions():
    acts=[]
    for i in range(5):
        acts.append(lambda x:i**x)
    return acts

acts=makeActions()
print(acts[0](2))   #i=4  4**2=16  
print(acts[1](2))   #i=4  4**2=16
print(acts[2](2))   #i=4  4**2=16
print(acts[3](2))   #i=4  4**2=16
print(acts[4](2))   #i=4  4**2=16

#如上程序,因为嵌套函数的作用域在被调用时才进行查找,因此i=4,所以实际上记住的都是同样的i,必须使用默认值参数传递
def makeActions():
    acts=[]
    for i in range(5):
        acts.append(lambda x, i=i:i**x)
    return acts

acts=makeActions()
print(acts[0](2))   #i=0  0**2=0
print(acts[1](2))   #i=1  1**2=1
print(acts[2](2))   #i=2  2**2=4
print(acts[3](2))   #i=3  3**2=9
print(acts[4](2))   #i=4  4**2=16

#nonlocal声明将要在一个嵌套作用域中修改的名称,使用nonlocal语句,嵌套的def可以对嵌套函数中的名称进行读取和写入访问
#nonlocal和global的不同之处在于,nonlocal应用于一个嵌套的函数的作用域中的一个名称,而不是所有def之外的全局模块作用域
#nonlocal申明名称时,他必须已经存在与该嵌套函数的作用域中——他们可能只存在于一个嵌套的函数中,并且不能由一个嵌套的def中的第一次赋值创建
#换句话说,nonlocal即允许对嵌套的函数作用域中的名称赋值,并且把这样的名称的作用域查找限制在嵌套的def,而非本地作用域

def tester(start):
    state=start
    def nested(label):
        nonlocal state    #声明state可以修改,如果不声明,则不能修改state
        state+=1  
        print(label, state)

    return nested

f=tester(0)
f('spam')   #state=1,state+=1
f('eggs')   #state=2,state+=1

******************************************************************************************

def tester(start):
    global state
    state=start
    def nested(label):
        #nonlocal state    #声明state可以修改,如果不声明,则不能修改state
        global state       #此时如此使用global可以达到和nonlocal一样的效果
        state+=1  
        print(label, state)

    return nested

f=tester(0)
f('spam')   #state=1,state+=1
f('eggs')   #state=2,state+=1




猜你喜欢

转载自blog.csdn.net/weixin_37016700/article/details/80228444