一.名称空间、作用域、取值顺序等。
1.(1)名称空间:当程序开始运行时,代码自上而下开始依次执行,他会将变量与值的对应关系存储在一个空间中,这个空间就叫名称空间,命名空间,全局名称空间,当程序遇到函数时,他会将函数名存在内存中,函数体莫不关心。当函数执行时,内存会临时开辟一个空间,存放函数体里面的代码(变量,代码等),函数外面访问不到临时空间的内容,随着函数的执行完毕,临时名称空间会释放掉,向这个临时开辟的空间叫临时名名称空间,也叫局部名称空间。
(2)python 中名称空间分三种:
内置名称空间。
全局名称空间。
局部名称空间。
(3)作用域:全局作用域:
内置名称空间。
全局名称空间。
(4)局部作用域
局部名称空间。
(5)加载顺序:
内置名称空间 ---> 全局名称空间(当程序执行时) ---> 局部名称空间(当函数调用时)
(6)取值顺序:单向不可逆
局部名称空间(当函数调用时) -->全局名称空间(当程序执行时) -->内置名称空间
(7).函数的嵌套操作:也就是说函数里面可以在定义多个函数嵌套起来使用,示例如下:
二.内置函数
1.内置函数的两个关键词:
globals() 返回一个字典,字典里面的内容是全局作用域的内容。
locals():返回一个字典,当前位置 的所有变量。
代码示例如下:
可以看到:无论在哪个位置,globals()打印出来的都是全局作用域的内容,而locals()就不一样,在函数里边,locals()打印的是函数里边的局部变量,而在函数外边则打印出来的是当前位置的全局变量。
案例二代码:
这里的答案就相当明显了:global()未打印出,因为当前没有全局变量,而locals打印出了当前位置的局部变量:name1和age1
三.全局变量和局部变量的使用:
这里先重复上一个概念:取值顺序:单向不可逆
局部名称空间(当函数调用时) -->全局名称空间(当程序执行时) -->内置名称空间
这里有两个关键词:global和nonlocal
(1).global:引用并改变一个全局变量,在局部作用域声明一个全局变量:
先看这样一个例子:
这样看程序是报错的,而报错原因是因为在函数里边并没有定义count变量,count = 1只是定义的一个全局变量,在函数里边并不能调用的到,但如果我们想用呢?这里就用到了global这个关键字:
这样,代码就能正常执行啦,global 的作用就是引用并改变一个全局变量,在局部作用域声明一个全局变量。
当然也可以在局部变量里声明变量,让它当做全局变量来使用:
(2)nonlocal:不能操作全局变量,从那层引用就从那层开始改变:
取值:引用而不是改变
取值是从小到大取值 LEGB
想改变上层空间的变量 要用到global nonlocal
(3).对于可变的数据类型,dict、list、set不能使用global/nonlocal参数。
如果默认参数是一个可变的数据类型,那么他在内存中永远是一个
四.函数名的应用。
(1).打印函数名:
(2).函数名可以作为容器类数据的元素:
(3).函数名可以作为函数的参数传递进来:
(4).函数名可以作为函数的返回值:
五.闭包:
(1).闭包:内层函数对外层函数非全局变量的引用,就叫做闭包
这里的inner函数就是内层函数,调用的name变量就是对外层函数非全局变量的引用。
这里有一个.__closure__方法就是判断是不是闭包,如果打印出cell之类的信息就是闭包,如下:
如果 python 解释器遇到了闭包,他有一个机制,这个闭包不会随着函数的结束而释放。
六.装饰器:
(1).什么是装饰器,装饰器本质就是闭包,就是在不影响原函数功能的前提下,增加一些额外的功能,如打印日志,执行输出的一些功能,接下来一步步引入装饰器的概念:
有这样的这一个需求:假设要测试一个函数的执行效率(即运行这个函数需要多长时间),我们首先想到的应该是这个样子:
版1:
这个时候你可能会发现这样程序不具有模块化,要将测试函数用函数封装起来更好一点:
版2:
这个时候又有问题了,假设有1000个测试函数,那我岂不是要写1000个timer函数,太冗余了,何不把测试函数当成一个参数传递进来呢,这就会更便捷一点:
版3:将测试函数以参数的形式传递进来
这个时候又有问题了,试想:我每测试一个函数就要按照这样的格式去写,那是不是就改变了我原有函数的执行方式了呢?这样不好,再次进行改版:
版4:尽量不要改变原来函数的执行方式
这样虽然大致满足了我的需求,但是调用的时候还是多传递了一个参数进来,这样不太好,有没有一种方法,不改变他的调用方式呢?继续改版
版5:不改变原来函数的运行方式:
这个时候就基本满足我们的要求啦,也没有改变原有函数的调用方式,但每次调用前我们总要先执行 fun = timer(fun)函数,太麻烦啦,python里边提供一个@符号也就是语法糖的概念代替这一句话:
版6:引入语法糖:
这样程序及基本算完事啦,还有一个问题,试想如果人家要测试的函数有参数怎么办?你总不能再次给人家开发一个timer吧,这里我们还需要加上参数
版7:加入参数
现在参数加进去了,但如果要有返回值该怎么办呢?简单,定义一个参数接收并返回就行了,继续改。。。
版8:加入返回值:
到这里就是一个完整的装饰器啦,既不需要改变原来函数的调用方式,也具有传递参数、返回值的功能!
装饰器的格式: