python学习——函数进阶

首先来看下面这个函数。

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
nonlocal 关键字


三、函数名的本质

  函数名本质上就是一个函数的内存地址(函数即变量)

  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)
闭包函数获取网页信息

猜你喜欢

转载自www.cnblogs.com/lcs-LLH/p/9939213.html