关于默认参数的陷阱问题:
见如下代码:
def func(m=[]):#默认参数的值是可变类型 m.append(1) print(m) func() func() func()
#控制台输出如下:
[1] [1, 1] [1, 1, 1]
即如果默认参数的值是一个可变数据类型,那么每一次调用函数的时候,如果不传值就共用这个数据类型的资源。
函数的命名空间
1:内置命名空间
就是python解释器一启动就可以使用的名字,存储在内置命名空间中
内置的名字在启动解释器的时候被加载进内存里
2:全局命名空间
是在程序从上到下被执行的过程中依次加载进内存的,放置了我们设置的所有变量名和函数名
3:局部命名空间
就是函数内部定义的名字
当调用函数的时候才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了。
在局部:可以使用全局,内置命名空间中的名字
在全局:可以使用内置命名空间中的名字,不能使用局部命名空间中的名字
在内置:不能使用全局,局部命名空间中的名字
多个函数应该拥有多个独立的命名空间,不互相共享
作用域:
全局作用域----作用在全局----内置和全局命名空间中的名字都属于全局作用域
局部作用域----作用在局部----函数(局部命名空间中的名字属于局部作用域)
对于不可变数据类型,在局部可以查看全局作用域中变量,但是不能直接修改,如果想要修改,需要在局部的一开始添加global声明,如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效。
例如如下代码:
a=1#声明了一个全局变量a,值为1 def func(): a=2#在局部(函数)改变a的值 func()#调用这个函数 print(a)#由输出结果可知局部的改变并没有影响到全局变量 #控制台输出 1
再看下面代码
a=1 def func(): global a#在上面代码的基础上,添加global声明 a=2 func() print(a)#由输出结果可知,在局部声明了global后,局部的变化影响到了全局变量 #控制台输出 2
函数的嵌套定义:
内部函数可以使用外部函数的变量(不可变类型),但也不能直接修改,想要修改,需要在内部函数的变量前添加nonlocal声明
def outer():#定义一个外部函数 a=1#在外部函数定义变量a def inner():#定义一个内部函数 b=2 print(a) print('inner') def inner2():#定义一个内部函数 print(a,b)#可以查看a,b的值,但是不能直接修改 print('inner2') inner2() inner() outer() #控制台输出 1 inner 1 2 inner2
如果企图在内部函数修改变量a
def outer(): a=1 def inner(): b=2 print(a) print('inner') def inner2(): a+=1#企图在内部函数修改a print(a,b) print('inner2') inner2() inner() outer()
#控制台输出:(报错)
UnboundLocalError: local variable 'a' referenced before assignment
在内部函数为变量a添加nonlocal声明后:
def outer(): a=1 def inner(): b=2 print(a) print('inner') def inner2(): nonlocal a#添加nonlocal声明,nonlocal只能用于局部变量,只会找上层中离当前函数最近一层的局部变量,如果最外层函数也没有这个变量,就会报错,不会去全局命名空间找,声明了nonlocal的内部函数的变量修改会影响到离当前函数最近一层的局部变量 a+=1 print(a,b) print('inner2') inner2() inner() outer()
输出为:
1 inner 2 2#可见a的值变化 inner2
函数名的本质:
函数名本质上就是函数的内存地址。
1.可以被引用(可以赋值)
def func(): print(123) func2=func#函数名可以赋值 func2()
#控制台输出:123
2.可以被当作容器类型的元素(容器类型即列表,元组,集合这些)
def func(): print(123) func2=func l=[func,func2]#函数名可以作为容器类型的元素 print(l) for i in l: i() #控制台输出 [<function func at 0x000001B98ED85950>, <function func at 0x000001B98ED85950>] 123 123
3.函数名可以作为函数的参数和返回值
#函数名作为函数的参数 def func(): print(123) def wahaha(f): f() wahaha(func)#函数名可以作为函数的参数 #控制台输出 123
#函数名作为函数的返回值 def func(): print(123) def wahaha(f): f() return f#函数名可以作为函数的返回值 qqxing=wahaha(func) qqxing() #控制台输出 123 123
所以函数是第一类对象,什么是第一类对象?
第一类对象(first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体。
不明白?那就记住一句话,就当普通变量使用
闭包:
内部函数包含对外部作用域而非全局作用域的名字的引用,该内部函数成为闭包函数。
def outer(): a=1 def inner():#这个函数就是一个闭包函数 print(a)#如果没有这句,这个函数就不是一个闭包函数,因为内部函数没有引用外部函数的变量 outer()
想要知道一个内部函数是不是闭包函数,需要使用__closure__方法。
def outer(): a=1 def inner(): print(a) print(inner.__closure__)#注意没有__closure__括号 outer() #控制台输出 (<cell at 0x000001D98A4EC078: int object at 0x00000000584F22D0>,) 有cell 什么什么的,就说明是一个闭包函数
我们都知道如果想要在函数外部使用函数内部的变量,只需返回这个变量,同理,如果我们想在函数外部使用函数内部嵌套的函数,只需将内部函数返回即可,这就是闭包最常用的方法
def outer(): a=1 def inner(): print(a) return inner#返回inner()函数的地址 inn=outer()#执行outer()函数,返回inner()函数的地址赋给inn变量 inn()#加上括号表示执行这个函数,inn变量里面是inner函数的地址 #控制台输出 1
闭包的好处:
若不使用闭包,则内部函数想要在外部调用必须如下操作:
def outer(): a=1 def inner(): print(a) inner()#必须写上这句 outer()#然后再外部执行外部函数
这样每次调用outer()函数都会在内存创建a变量,结束调用后又删除a变量,浪费了很多时间,若使用闭包,当执行inn=outer()时,内部函数的地址就被inn变量保存,当结束调用后python解释器会认为变量a还有用,所以不会删除,这样以后每次要使用内部函数时,只要写inn()就可以,节省了时间。