函数的命名空间和作用域
一、函数的命名空间
命名空间:name space是从名称(name)到对象(object)上的映射。
当一个name映射到一个object上时,这个name和这个object就有了绑定(bind)关系,或者说这个name指向了这个object。
每个name只对应一个object,而一个object可有多个名字。这就是类与实例的关系。
不同的命名空间在不同的时刻被创建,并且有不同的生命周期。
内置的命名空间在python解释器启动的时候创建的,并且一直保留,不会删除。
在python程序中的任何地方,都存在着命名空间。
python中有三种命名空间:
局部命名空间:函数内的名称空间就是局部的。
全局命名空间:写在函数外面的变量名。模块内的命名空间就是全局的。
内置命名空间:python解释器启动时创建的,包括异常内型、内建函数和特殊方法,可以在代码中的任何地方调用。
函数外面的变量可以在函数里面调用,函数里面的变量不能在外边用,因为有独自的作用域。
注意循环体内不会创建作用域。
名称空间的加载顺序:
a.python解释器首先运行起来,加载所有内置命名空间中的名字。
b.然后按照顺序加载全局命名空间
c.局部命名空间随函数的调用而创建,随着函数的结束而消除。
命名空间的查找顺序:
a.如果在函数内调用一个变量,先在函数内(局部命名空间)查找,如果没有找到则去函数外部(全局命名空间)查找,如果还是没有找到,则去内置命名空间中查找,内置空间再找不到那就只有报错。
b.如果在函数外调用一个一个变量,先查全局命名空间,也就是本地,如果没有找到,那就去内置命名空间中查找,这里找不着也没辙。
二、函数的作用域
作用域:一个名字可以使用的区域范围。
全局作用域:内置名字空间和全局名字空间中的名字都属于全局作用域
局部作用域:局部名字空间中的名字属于局部作用域。
局部作用域可以使用全局作用域中的变量
而全局作用域不能使用局部作用域中的变量
局部作用域中的变量又可以被更小的作用域所调用
作用域链:小范围作用域可以使用大范围的变量,但作用域链是单向的,不能反向。
globals():保存了在全局域中的名字和值,无论在哪打印值都一样
locals():保存了当前作用域中变量,根据执行的位置来决定作用域中的内容
如果在全局执行,globals()和locals()的结果是相同的
global :声明参数是全局变量
nonlocal :调用最近的这个参数
用几个例子来演示:
(1)代码的加载顺序
#代码执行的顺序 def func2(): #1 读取函数 print('my name is func2') #5 print('多写一行') #6 if True: #7 print('又多写了一行') #8 return 'func2的返回值' #9 def func(): #2 ret = func2() #4 调用函数 print(ret) #10 n = 20 #11 print(n) #12 func() #3 执行函数
(2)函数的镶嵌
def func(): def kebi(): print('我是科比.布莱恩特') kebi() #要想调用kebi,必须先要再func()里面调用 func()
(3)作用域
n = 1 def func(): print(n) #会报错,在圈子里不能改变圈子外的值 n = n+1 func()
#理解下面
n = 100
def func():
n = 200
print(n)
func()
#上面代码不会报错,本质原因是有2个n,局部作用域与全局作用域各一个
n = 1 def func(): global n #用global来声明这是一个全局变量 n = n+1 print(n) func() 结果: 2
如果想调用局部中的变量
n = 0 def func1(): n = 1 def func2(): nonlocal n #调用就近的一个局部变量,不会调用全局变量 n +=1 func2() print(n) func1() print(n) 结果: 2 0
三、函数的名字
函数名可以赋值
def foo(sr): print(sr*2) bar = foo bar(123) 结果: 246
可以作为一个数据结构的元素
def foo(sr): print(sr*2) lst = [1,2,foo,5] print(lst) 结果: [1, 2, <function foo at 0x000002DD65CE4048>, 5] <function foo at 0x000002DD65CE4048> foo的内存地址
函数可以作为一个函数的参数
def foo(sr): print(sr*2) def bar(lst): return lst print(bar(foo)) 结果: <function foo at 0x000002AE437C4048>
函数的名字可以作为一个函数的返回值
def foo(sr): print(sr*2) def bar(): return foo print(bar()) 结果: <function foo at 0x000001DF93844048>