在这里我们首先回忆一下python代码运行的时候遇到函数是怎么做的。

从python解释器开始执行之后,就在内存中开辟了一个空间

每当遇到一个变量的时候,就把变量名和值之间的对应关系记录下来。

但是当遇到函数定义的时候解释器只是象征性的将函数名读入内存,表示知道这个函数的存在了,至于函数内部的变量和逻辑解释器根本不关心。

等执行到函数调用的时候,python解释器会再开辟一块内存来存储这个函数里的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。

我们给这个“存放名字与值的关系”的空间起了一个名字——叫做命名空间

代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间

命名空间一共分为三种:

#局部命名空间:每一个函数都拥有自己的命名空间


#全局命名空间:写在函数外面的变量名


#内置命名空间:python解释器启动之后就可以使用的名字

 

*内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。

三种命名空间之间的加载与取值顺序:

加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

 

def func2():
    print('in func2 now')
    print('多写的一行')
    if True:
        print('又多写了一行')
    return 'func2的返回值'

func2()

输出:
in func2 now
多写的一行
又多写了一行

 

def func2():
    print('in func2 now')
    print('多写的一行')
    if True:
        print('又多写了一行')
    return 'func2的返回值'

def func():
    ret=func2()
    print(ret)
    n = 20
    print(n)

func()

输出:
in func2 now
多写的一行
又多写了一行
func2的返回值
20

 

作用域

作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

局部作用域:局部名称空间,只能在局部范围生效

#作用域:一个名字可以使用的区域
#全局作用域:内置名字空间和全局名字空间中的名字都属于全局作用域
#局部作用域:局部名字空间中的名字属于局部作用域
#局部作用域可以使用全局作用域中的变量
#而全局作用域不能使用局部作用域中的变量
#局部作用域中还可以嵌套更小的局部作用域
#作用域链:小范围作用域可以使用大范围的变量,但作用域链是单向的,不能反向应用

n = 0
def func1():
    n = 1
    def func2():
        n = 2
        def func3():
            n = 3
            print(n)
        func3()
    func2()

func1()

输出:
3


globals和locals方法

globals()     #保存了在全局作用域中的名字和值
locals()       #中的内容会根据执行的位置来决定作用域中的内容
如果在全局执行
globals和locals打印的结果是一致的

n = 1
def func():
    global n
    n = n+1

func()
print(n)

输出:
2

 

#函数的嵌套调用和函数的嵌套定义
#命名空间:三种:内置 全局 局部
#作用域:两张:全局 局部
#作用域链:名字的使用 是从小范围到大范围的就近事件
#globals和locals方法
#小范围可以使用大范围的,但不能修改
    #如果想要修改全局的:使用global关键字  —— 尽量避免
    #如果想要修改最近拥有该变量的外层函数的:使用nonlocal   ——不影响全局

n = 0
def func():
    n = 1
    def func2():
        #n = 2
        def func3():
            nonlocal n
            n+=1
        func3()
        print(n)
    func2()
    print(n)

func()
print(n)

输出:
2
2
0

 

 

n = 0
def func():
    n = 1
    def func2():
        n = 2
        def func3():
            nonlocal n
            n+=1
        func3()
        print(n)
    func2()
    print(n)

func()
print(n)

输出:
3
1
0

 

函数的嵌套调用和作用域链:

def max2(x,y):
    m  = x if x>y else y
    return m

def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    return res3

print(max4(23,-7,31,11))

输出:
31

nonlocal关键字

1.外部必须有这个变量 
2.在内部函数声明nonlocal变量之前不能再出现同名变量
3.内部修改这个变量如果想在外部有这个变量的第一层函数中生效
def f1():
    a = 1
    def f2():
        nonlocal a
        a = 2
    f2()
    print('a in f1 : ',a)

f1()

输出:
a in f1 :  2

小结

命名空间:

  一共有三种命名空间从大范围到小范围的顺序:内置命名空间、全局命名空间、局部命名空间

作用域(包括函数的作用域链):

小范围的可以用大范围的
但是大范围的不能用小范围的
在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的
如果在小范围内没有,就用上一级的,上一级没有就用上上一级的,以此类推。
如果都没有,报错

函数的嵌套:

  嵌套调用

  嵌套定义:定义在内部的函数无法直接在全局被调用

函数名的本质:

  就是一个变量,保存了函数所在的内存地址