函数的进阶知识

函数进阶

主要内容: 

  • 函数作为对象
  • 函数的嵌套调用与定义
  • 名称空间
  • 名称空间的查找顺序
  • 作用域

 

1. 函数对象

  函数在Python中是一种特殊的存在,它们可以被当做对象来使用,这个重要的特性就使得函数多了许多特殊的功能。

  其实函数名就相当于是一个指向保存函数体的内存地址,我们通过访问这个函数名,可以直接得到函数地址,通过函数名加括号就可以正式调用这个函数,来实现函数具体的功能。函数名既然只是一个引用,就像普通变量一样,就多了许多操作空间,具体有以下几种

  1.1.  函数名可以像普通数据类型当参数一样传递

  这句话的意思是就比如我们可以通过print函数打印函数名,就会出现一个函数地址相关的信息,也可以通过id方法查看函数地址。这也说明了函数名只是一个引用,是引用就可以被其他函数接收,也可以为其他变量赋值。具体情况如下图所示

           

  1.2. 函数名可以被当做其他函数的返回值返回

  这个用法很特殊,是后面闭包装饰器的基础原理。当函数名被当做返回值返回了,我们就可以使用一个变量名接收,那么这时候新的变量名就相当于也指向了这个函数的内存地址,也具有了同函数一样的功能,只是换了个名字而已。

  1.3.  函数名可以作为容器类型的值

  函数名可以作为列表的值,也可以作为字典的值等等。 这个用法比较重要,比如当我们可以通过这个用法把各种不同的函数功能整合成一个接口,这样就防止多个if else语句来判断,再执行相应功能了。

def login():
    print('login')


def register():
    print('register')


def index():
    print('index')


operations = [
    ('登录', login),
    ('注册', register),
    ('首页', index),
]

for idx, op in enumerate(operations):
    print(idx, op[0])

choice = int(input('>>> ').strip())
operations[choice][1]()
0 登录
1 注册
2 首页
>>> 0
login

  上面这个例子把函数引用放入了一个列表中,我们通过列表的下标来选择相应执行的功能,这样就大幅度减少了代码的冗余,可扩展性也好了很多。

2.  函数的嵌套

  2.1 函数的嵌套调用

  所谓函数的嵌套调用很简单,就是函数在定义后,可以在任何可以调用的地方调动,包括函数体内,这就像内置函数print,input等等,可以在任意地方调用。

  2.2 函数的嵌套定义

  函数的嵌套定义,顾名思义,就是在函数体内部再重新定义函数,这个其中一个功能就是可以在只使用一个函数接口,然后根据传入的参数不同,而调用不同内部函数功能,和上面的把函数名放到列表里面有异曲同工之妙。具体嵌套调用就如下面所示,比较简单。

def func():
    print(func)

    def inner():
        print('inner')
    return inner


func()()
<function func at 0x000002C490012E18>
inner

3. 名称空间

   名称空间的定义通俗点来讲就是存储变量名函数名等等名字和相应内存地址的绑定关系的地方。

  名称空间通常可以分为3种类型:

  1. 内置名称空间  主要存储内置函数的地方,内置名称空间在Python解释器启动后就一直有效,直到解释器退出才会销毁。

  2. 全局名称空间 存储全局变量或是和全局变量同级别的函数类等等的地方,全局名称空间只有在py文件运行时才生效,py文件结束结束时也就结束了生命周期了。

  3. 局部名称空间  主要就是函数体所在的地方,它的生存周期只在函数调用时才生效,函数调用结束后自动销毁。

4. 查找顺序查找顺序

  名称空间查找顺序总体讲究就近原则。即 L (local 局部变量空间)--> G(全局变量空间)  ---> B(内置名称空间),查找一个变量名会按照这个顺序去查找相对应的命字,都找不到就会报错。

print(max)

max = 1

def test():
    max = 2
    return max

print(max)
print(test())
<built-in function max>
1
2

  上面这个例子也比较清晰的表明了变量查找顺序就是按照就近原则寻找, 也就是按照LGB规则查找的。

关于变量查找的特殊例子。

  看下面一个例子

def test():
    x = 1
    def t1():

        def t2():
            # x = 3
            print(x)
        t2()
        x = 2
    t1()
test()

  此处执行后会报NameError: free variable 'x' referenced before assignment in enclosing scope的错误,这是为什么呢?

这是我的分析,Python程序从上到下执行,先定义函数,由于定义函数阶段有检测语法,不执行代码的功能,所以在t2的命名空间里已经有了x这个变量的名称,但是由于没有执行赋值语句,所以相当于不知道引用的特殊变量。当我们调用test函数,最后去执行t2函数,要寻找x变量,按照就近原则,最近的就是t1所在的名称空间,但此时赋值在后,所以会报以上这个错误。

5. 作用域

  理解了名称空间,作用域的概念就比较好理解了,简而言之就是变量的左永刚范围。局部名称空间里的变量,包括嵌套的函数等等只在局部有效,其他地方都不能获取。全局名称空间的变量在全局有效,包括在局部名称空间也可以访问。内置名称空间就是所有地方都可以访问了。

  关于作用域有两个关键字参数 global和nonlocal

  global 可以在局部修改全局变量

  nonlocal 可以在局部的局部修改局部变量,即函数嵌套中可以使用

 

posted @ 2019-07-10 22:23  yscl  阅读(223)  评论(0)    收藏  举报