一. 知识仓库
- 默认参数的陷阱:如果默认参数的值是一个可变数据类型,那么每次调用的时候不传值,就公用这个数据类型的资源
- 函数的命名空间
1、内置命名空间:
- python解释器一启动就将可以使用的名字存储在内置命名空间中,内置的名字(如print、input、str…)在启动解释器的时候被加载进内存中
- 与python解释器有关
2、全局命名空间:
- 是在程序从上到下被执行的过程中依次加载进内存的,放置了我们设置的所有变量名和函数名
- 与我们写的代码有关,但不包括函数中的代码
3、局部命名空间:
- 是函数内部定义的名字,在函数被调用的时候才会产生这个名称空间,函数执行结束后该空间被回收(消失)
- 与函数内部的名义有关
4、注意:
- 在局部:可以使用全局、内置命名空间中的名字
- 在全局:可以使用内置命名空间中的名字,但是不能使用局部命名空间中的名字(函数执行结束后该空间被回收)
- 在内置:不能使用局部、全局命名空间中的名字(在读代码和调用函数之前就已经启动了)
- 依赖倒置原则:小的可以依赖大的,但大的不能依赖小的。该处内置为大,局部为小。
- 当我们在全局定义了和内置空间中同名的名字时,会使用全局的名字(即当自己有的时候,就不找自己的上级要,一直到内置也没有该名字,就报错)
- 局部和全局命名空间只有一个,而局部命名空间可能有多个(多个函数),且各个局部命名空间中的名字不共享,是相互隔离的 - 函数的执行(调用):func()本质上是函数的内存地址+(),即函数的内存地址(),而外在表现的func刚好指向了该函数的内存地址(见function_2.py)
- 作用域
1、全局作用域:作用在全局,内置和全局命名空间中的名字属于全局作用域
2、局部作用域:作用在局部,函数(局部空间)中的名字属于局部作用域
3、注意:
- 对于不可变数据类型,在局部可以查看全局作用域中的变量,但不能直接去修改,修改则需要在程序的一开始添加global声明)(见function_2.py)
- 如果在一个布局(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效,出于安全应该避免使用global - 两个方法:locals() ——> 查看该方法所在作用域中的所有名字、globals() ——> 查看全局作用域中的所有名字(内置命名空间+全局命名空间)
- 函数的嵌套和作用域链
1、函数的嵌套调用:在函数里面调用函数
2、函数的嵌套定义:
- 内部的函数可以使用外部函数的变量
- 内部函数在定义之后需要进行调用,否则会在外部函数调用之后消失而不产生作用
- 同样的,对于不可变数据类型,内部函数只可以使用外部变量,但不可修改
- 如果内部函数用到global则是修改全局变量,也不会对外部函数(局部)定义的a产生作用(见function_2.py)
- 内部函数修改外部函数的变量:使用 nonlocal 进行声明,其作用范围是向上找到的第一个该局部变量(注意局部!),到最外层局部也找不到就报错 - 函数名的本质
1、函数名本质上就是指向该函数的一段内存地址
2、函数名是第一类对象
- 函数名可以作为函数的返回值和参数
- 函数名可以作为容器类型的元素
- 函数名可以赋值
3、第一类对象(first-class object)
- 可在运行期创建
- 可用作函数参数或返回值
- 可存入变量的实体 - 闭包
1、条件:嵌套函数,内部函数调用外部函数的变量
2、常见的闭包的使用方法:在一个函数的外部去使用这个函数内部的函数
3、使用闭包的好处:保护了局部变量,减少了局部变量频繁的创建和消散而造成的浪费
二. 代码练习
"""
默认参数的陷阱
- 如果默认参数的值是一个可变数据类型,那么每次调用的时候不传值,就公用这个数据类型的资源
"""
def func1(l1=[]):
l1.append(1)
print(l1)
func1() # [1]
func1([]) # [1]
func1() # [1, 1]
func1() # [1, 1, 1]
# 列表是可变数据类型
def func2(k, dic1={
}):
dic1[k] = 'v'
print(dic1)
func2