首先来看下面这个函数。
1 def func(x,y): 2 bigger = x if x > y else y 3 return bigger 4 ret = func(10,20) 5 print(ret) 6 7 #运行结果 : 20
在上面的函数中我们把较大值通过return这个关键字返回回来了,如果我不返回而是直接打印可不可以?如下:
def func(x,y): bigger = x if x > y else y func(10,20) print(bigger) #运行结果 : NameError: name 'bigger' is not defined
此时它会说,bigger没有定义,这是为什么,在函数中我明明定义了bigger就是较大的那个数,那问题出在哪儿呢?
在这里我们首先回忆一下python代码运行的时候遇到函数是怎么做的。从python解释器开始执行之后,就在内存中开辟了一个空间,每当遇到一个变量的时候,就把变量名和值之间的对应关系记录下来。但是当遇到函数定义的时候解释器只是象征性的将函数名读入内存,表示知道这个函数的存在了,至于函数内部的变量和逻辑解释器根本不关心。等执行到函数调用的时候,python解释器会再开辟一块内存来存储这个函数里的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被解释器释放了。
我们给这个“存放名字与值的关系”的空间起了一个名字——叫做命名空间
代码在运行的时候,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间
一、命名空间和作用域
首先看一下这张图:
这是python之禅中说的,命名空间非常好!在python中命名空间有以下几种:
内置命名空间
全局命名空间
局部命名空间
内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。
三种命名空间之间的加载与取值顺序:
加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
取值:
在局部调用:局部命名空间->全局命名空间->内置命名空间
1 a = 1 2 b = 2 3 def func(): 4 print(a) 5 print(b) 6 func() 7 print(10) 8 9 #运行结果 :1,2,10
所以存在这种情况:当调用函数时,函数被执行,但是函数内部(局部)并没有a,b这两个值,只能道函数外面(全局)来找,找到了,就答应,而之后打印的10在全局中就直接找到了。
在全局调用:全局命名空间->内置命名空间
1 a = 1 2 b = 2 3 def func(a,b): 4 print(a) 5 print(b) 6 7 func(10,20) 8 print(a,b) 9 10 # 运行结果:10,20,1 2
此时对a,b传值了,所以首先答应出来,在全局再次打印a,b的时候,函数内部的a,b在函数执行完毕之后就被释放了,所以只能打印全局a,b的值;还有即使a,b没被释放,在全局也不能打印局部变量的值!
1 print(max(1,2,3,3)) 2 3 #结果:3
所以三种命名空间有以下这种关系:
所以他们的关系是这样的,内置命名空间在解释器一打开就被加载到内存中了,在定义函数之前的所有变量都是全局命名空间中的变量,而在函数内部的所有变量都是局部命名空间中的变量,当然只有函数被调用时才被加载到内存中,但随着函数执行完毕就被自动释放了。
二、函数的嵌套和作用链域
函数的嵌套调用
1 def func1(a,b): 2 return a if a > b else b 3 4 def func2(x,y,z): 5 ret1 = func1(x,y) 6 ret2 = func1(ret1,z) 7 return ret2 8 9 ret = func2(1,2,3) 10 print(ret) 11 12 #运行结果:3
函数的嵌套定义
1 # 函数的嵌套定义 一 2 3 def func1(): 4 print('in func1 now') 5 def func2(): 6 print('in func2 now') 7 func2() 8 9 func1() 10 11 # 函数的嵌套定义 二 12 13 def func1(): 14 def func2(): 15 def func3(): 16 print('in func3 now') 17 func3() 18 print('in func2 now') 19 func2() 20 print('in func1 now') 21 func1()
函数的作用链域
1 # 函数的作用链域 一 2 def func1(): 3 a = 1 4 def func2(): 5 print(a) 6 func2() 7 func1() 8 9 # 运行结果:1 10 11 # 函数的作用链域 二 12 def func1(): 13 a = 100 14 def func2(): 15 def func3(): 16 print(a) 17 func3() 18 func2() 19 20 func1() 21 22 # 运行结果:100 23 24 # 函数的作用链域 三 25 def func1(): 26 a = 1000 27 def func2(): 28 a = 10000 29 def func3(): 30 print('a in func1 ',a) 31 func3() 32 func2() 33 34 func1() 35 36 # 运行结果:a in func1 10000
nonlocal 关键字
1.外部必须有这个变量
2.在内部函数声明nonlocal变量之前不能再出现同名变量
3.内部修改这个变量如果想在外部有这个变量的第一层函数中生效
1 def func1(): 2 a = 1 3 def func2(): 4 nonlocal a 5 a = 2 6 func2() 7 print(a) 8 func1() 9 10 # 运行结果:2
三、函数名的本质
函数名本质上就是一个函数的内存地址(函数即变量)
1.可以被引用
1 def func(): 2 print('in func now ') 3 4 ret = func 5 print(ret) 6 7 # 运行结果:<function func at 0x0000023C04CDC730>
当打印函数名的时候返回的是一个内存地址。
2.可以被当作容器类型的元素
1 def func1(): 2 print('func1') 3 4 def func2(): 5 print('func2') 6 7 def func3(): 8 print('func3') 9 10 l = [func1,func2,func3] 11 d = {'func1':func1,'func2':func2,'func3':func3} 12 #调用 13 l[0] 14 print(l[0]) 15 l[0]() 16 print(d['func2']) 17 d['func2']() 18 19 #运行结果:<function func1 at 0x000001F345C0C7B8> func1 <function func2 at 0x000001F345C0C840> func2
当我们不调用时(不在后面加上“()”),返回函数名所在的内存地址,加上之后返回函数值。
3.可以当作函数的参数和返回值(就是把函数名当作普通变量来用)
第一类对象(first-class object)指 1.可在运行期创建 2.可用作函数参数或返回值 3.可存入变量的实体。
四、闭包函数
1、首先说什么是闭包
1 def func1(): 2 name = 'liulonghai' 3 def func2(): 4 print(name)
2、闭包函数
内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数 #函数内部定义的函数称为内部函数
判断一个函数是否是闭包可以这样写
1 def func1(): 2 name = 'liulonghai' 3 def func2(): 4 print(name) 5 print(func2.__closure__) 6 print(func1.__closure__) 7 func2() 8 9 func1() 10 11 #运行结果:liulonghai 12 # (<cell at 0x000001B30E5F0108: function object at 0x000001B31163D7B8>, 13 # <cell at 0x000001B3103A7378: str object at 0x000001B3103A0370>) 14 # None
可以通过(__closure__)这个双下方法来查看一个函数名是不是闭包,当打印出"(<cell at 0x000001B30E5F0108: function object at 0x000001B31163D7B8>, <cell at 0x000001B3103A7378: str object at 0x000001B3103A0370>)" 这样就表面此函数是一个闭包,其实就是‘cell’ ,如果不是,则返回None
由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们就是想拿怎么办呢?返回?我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?是不是直接就把这个函数的名字返回就好了?
这才是闭包函数最常用的用法
1 def func(): 2 name = 'liulonghai' 3 def inner(): 4 print(name) 5 6 return inner 7 8 f = func() 9 f() 10 11 #运行结果:liulonghai
闭包函数的嵌套
1 def wrapper(): 2 name = 'liulonghai' 3 def outter(): 4 money = 1000000 5 def inner(): 6 print(name,money) 7 return inner 8 return outter 9 10 w = wrapper() 11 o = w() 12 o() 13 14 #运行结果:liulonghai 1000000
闭包函数获取网页
1 from urllib.request import urlopen 2 3 def index(): 4 url = "http://www.baidu.com" 5 def get(): 6 return urlopen(url).read() 7 return get 8 9 baidu = index() 10 content = baidu() 11 print(content)