函数的命名空间和作用域
命名空间
定义:可以理解为一个容器,在这个容器中可以装许多个名字。不同容器中的同名的名字是不会相互冲突
命名空间的三种形式
内置命名空间 全局命名空间 局部命名空间
内置 在启动解释器的时候生成的,存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法
全局 文件级别定义的名字都会存放在全局命名空间,执行python文件时会产生,文件执行完毕就会失效
文件级别:定义的名字:顶头写的没有缩进的名字
局部级别:定义在函数内部的名字,只在调用函数时才会生效,函数调用完毕就会失效
三种命名空间之间的加载与取值顺序
加载顺序:
内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
取值顺序:
(1) 在局部调用:局部命名空间->全局命名空间->内置命名空间
1 x = 1 2 def f(x): 3 print(x) 4 print(10) 结果为10
注释:执行代码 第一步x=1 定义函数f并传入形参x 第三步 打印x 第四步
(2)在全局调用:全局命名空间->内置命名空间
1 x = 1 2 def f(x): 3 print(x) 4 5 f(10) 6 print(x)
函数的作用域:
作用域定义:作用域就是作用范围
作用域分类
1 全局作用域: 包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效 2 局部作用域: 包含局部名称空间,只能在局部范围内生效
作用域中的关键字
globals查看内置和全局的所有变量名字,
locals查看局部的所有变量名字:当把locals放到全局的位置时可以查看全局的所有变量名字,当把locals放到局部的位置时就可以查看局部范围的所有变量名字
作用域的作用范围
globals 全局的作用范围
locals 局部的作用范围
函数的嵌套和作用域链
函数嵌套的作用
作用:为了保护内部函数,确定内部函数只能在外部函数中被调用
函数的嵌套调用
2 print('f1') 3 def f2(): 4 a = 10 5 f1() 6 f2() 7 #代码从上到下读取,f2()调用会调用f2()函数体的内容,最后发现f1()然后调用f1()开始执行函数体的内容最后输出f1
示例二:
1 def animal(): 2 def tiger(): 3 print('bark') 4 print('eat') 5 tiger() 6 animal() #函数嵌套调用 7 8 9 eat 10 bark
函数的嵌套定义
1 def f1(): 2 def f2(): 3 def f3(): 4 print("in f3") 5 print("in f2") 6 f3() 7 print("in f1") 8 f2() 9 10 f1()
函数的作用域链
1 def f1(): 2 a = 1 3 def f2(): 4 print(a) 5 f2() 6 7 f1() 结果为1
2 a = 1 3 def f2(): 4 a = 2 5 f2() 6 print('a in f1 : ',a) 7 8 f1() 结果 1 2
nonlocal关键字
2 a = 1 nolocal向上改变一层到这里,a的值被改变为2 3 def f2(): 4 nonlocal a 改变变量a的值并且只可以向上改变一层 5 a = 2 改变后的值为2 6 f2() 7 print('a in f1 : ',a) 8 9 f1() 结果 2 2 10 #函数会使用自己本身的a的赋值,f2()函数体里面的赋值用完会从内存删除掉,想保留要加入nonlocal 加变量名才能会取f2()函数体里a的赋值
函数名的本质
函数名本质上就是函数的内存地址
函数名的作用
1.可以被引用
print('func') #打印字符串的内容为func
print(func) #打印变量名func的内存地址
f=func #相当于定义了一个变量,名字是f 值为func
print(f) #打印f的内存地址
f() 调用f
#结果
#<function func at 0x02A94390>
#<function func at 0x02A94390>
#func
12 #func会指向函数的内存地址,不会取用函数里面的值,只有加()才能调用函数
2.可以被当作容器类型(可变数据类型)的元素
2 print('func') 3 print(func) 4 f=func 5 print(f) 6 l = [f] 7 print(l) 8 l[0] == f 9 l[0]() 10 11 12 <function func at 0x0000025B3C73B9D8> 13 <function func at 0x0000025B3C73B9D8> 14 [<function func at 0x0000025B3C73B9D8>] 15 func
3.可以当作函数的参数和返回值
1 第一类对象(first-class object)指 2 1.可在运行期创建 3 2.可用作函数参数或返回值 4 3.可存入变量的实体。
总之就记住一句话,就当普通变量用
闭包
闭包的定义
内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数
1.闭 内部的函数
2.包 包含了对外部函数作用域中变量的引用
由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们就是想拿怎么办呢?返回呀!
我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?
是不是直接就把这个函数的名字返回就好了?
闭包函数最常用的用法
2 name = 'eva' 3 def inner(): 4 print(name) 5 return inner 6 7 f = func() 8 f()
判断闭包函数的方法.__closure__
2 name = 'Eden' 3 def inner(): 4 print(name) 5 print(inner.__closure__) 6 return inner() 7 func() #看输出是否有cell元素有就是闭包函数 8 9 10 (<cell at 0x000002711EE465B8: str object at 0x000002711EEDA500>,) 11 Eden 12 16 #输出的__closure__为None :不是闭包函数 17 name = '山鹰' 18 def func2(): 19 def inner(): 20 print(name) 21 print(inner.__closure__) 22 return inner 23 24 f2 = func2() 25 f2() 26 28 None 29 山鹰
闭包函数的嵌套
2 money = 1000 3 def func(): 4 name = 'Eden' 5 def inner(): 6 print(name,money) 7 return inner 8 return func 9 10 f = wrapper() 11 i = f() 12 i()
闭包函数在网络上的应用
1 from urllib.request import urlopen 2 3 def index(): 4 url = "http://www.xiaohua100.cn/index.html" 5 def get(): 6 return urlopen(url).read() 7 return get 8 9 xiaohua = index() 10 content = xiaohua() 11 print(content)
小结:
命名空间:
有三种命名空间从大到小的范围顺序:内置命名空间、全局命名空间、局部命名空间
作用域(包括函数的作用域链):
调用时,如果在自己的空间内有,就用自己的。如果没有就使用大范围的。不能从大范围中用小范围的。
函数的嵌套:
嵌套调用
嵌套定义:定义在内部的函数无法直接在全局被调用
函数名的本质:
就是一个变量,保存了函数所在的内存地址
闭包(必须理解掌握,闭包是为后面装饰器的出现打基础):
内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数