函数嵌套、名称空间与作用域
一、可变长参数
可变长参数是指,调用函数的时候传入的实参个数不固定
而实参是为形参赋值的,相应的也要有新的形参格式来接收溢出的实参
1.1 形参名前加*
*号会把溢出的位置实参存成元组,然后赋值给后面的形参名
*后面的形参名通常使用args
如:
def func(x, y, *args):
print(x, y, args)
func(1, 2) # 1 2 () *z可以不被赋值
func(1, 2, 3, 4, 5) # 1 2 (3, 4, 5) 实参个数可以溢出,但是不能少于位置实参的个数,
1.2 形参名前加**
**号会把溢出的关键字实参存成字典,然后赋值给后面的形参名
**后面的形参名通常使用kwargs
如:
def func(x, y, **kwargs):
print(x, y, kwargs)
func(1, 2, a=111, b=222, c=333) # 1 2 {'a': 111, 'b': 222, 'c': 333}
func('name' = 'jason', x = 1, y = 2, 'age' = 18) # 1 2 {'name':'jason','age':18}
1.3 实参前加*
*会把其后的实参打散成位置实参
*号后跟的那个值应该是一个可以被for循环遍历的值
如:
func(*(111,222,333)) # func(111,222,333)
func(*"hello") # func("h","e","l","l","o")
1.4 实参中带**
**会把后面的那个值打散成关键字实参
**后跟的那个值应该是一个字典
如:
func(**{"x": 111, "y": 222, }) 就相当于 func(y=222,x=111)
1.5 在形参与实参中混用*、**
形参中*和**是汇总操作
实参中*和**是打散操作
如:
def func(*args, **kwargs): # args = (1,2,3) kwargs = {'a':1, 'b':2}
print(args, kwargs) # (1, 2, 3) {'a': 1, 'b': 2}
func(*(1, 2, 3,), **{'a': 1, 'b': 2}) # func(1, 2, 3, 'a'= 1, 'b'= 2})
1.6 命名关键字形参(了解)
def func(x, y=222, *args, n=777,m, **kwargs): # m,n必须按照关键字实参的格式为其赋值,其中n可以不被再次赋值
print(x) # 1
print(y) # 2
print(args) # (3,4,5)
print(m) # 66666
print(n) # 88888
print(kwargs) # {'a':11, 'b':22, 'c':33}
func(1, 2, 3, 4, 5, n=88888,m=66666, a=11, b=22, c=33)
二、函数对象
在python中,函数时第一类对象,简称函数对象
函数对象指的是函数可以当成变量一样去使用
2.1 可以被赋值
def func():
print('from func')
f = func # 函数名func所指向的的内存地址绑定给了f
f() # f()也可以进行函数调用
2.2 可以当做一个参数传给另一个函数
def func(aaa): # aaa=函数foo的内存地址
print(aaa)
aaa() # 只能在func()内调用aaa()
def foo():
print('from foo')
func(foo) # 将foo赋值给了变量aaa,那么aaa()也可以当做foo()使用来调用函数
2.3 可以当成一个函数的返回值
def func(aaa): # aaa=函数foo的内存地址
return aaa # 返回的是函数foo的内存地址
def foo():
print('from foo')
res=func(foo) # 将foo赋值给aaa
res() # 相当于foo()
2.4 可以当做容器类型的元素
def foo():
print('from foo')
l=[foo]
l[0]() # 即 foo(),一样可以调用函数
三、函数的嵌套
3.1 函数的嵌套调用
在调用一个函数的过称中又调用了另一个函数
如取四个值的中的最大值:
def max2(x, y):
if x > y:
return x
else:
return y
def max4(a, b, c, d):
res1 = max2(a, b)
res2 = max2(res1, c)
res3 = max2(res2, d)
return res3
print(max4(1, 2, 3, 4))
3.2 函数的嵌套定义
在一个函数内又定义了另一个函数
如:
def f1():
def f2():
print('from f2')
f2()
f1()
特点:
嵌套定义在函数内的函数,正常情况下,只能在函数体内调用
四、名称空间与作用域
4.1 名称空间
即存放名字的地方
1、内置名称空间
存放的是内置的名称,如:print、int、len
生命周期:解释器启动就产生,期间一直存在,解释器关闭就销毁
2、全局名称空间
存放的是顶级代码中的名字
生命周期:python程序运行时创建,程序顶级代码运行结束时销毁
3、局部名称空间
存放的是函数内的名字
生命周期:函数调用时创建,调用完毕就立即销毁
4.2 名称空间的查找优先级
基于现在的位置向外查找
具体的:局部—>外层套的局部—>外层套的局部—>…—>全局—>内置
ps:名称空间的嵌套关系是函数定义阶段(检测语法的时候)就已经确定好的,与调用的位置无关
如:
x = 111
def f1():
print(x) #定义f1时,x=111, 后面x如何变化都与f1内的x绑定的值(内存地址)无关
def f2():
x = 222
f1()
f2() # 111
4.3 作用域
① 全局作用域/全局范围:内置名称空间、全局名称空间
特点:全局存活、全局有效
② 局部作用域/局部范围:局部名称空间
特点:临时存活,局部有效
4.4 函数参数的传递
1、对于全局定义的不可变类型,不可以在函数内直接修改
x = 10
def func(a): # 调用时a=10的内存地址
a = 123 # 123的存地址绑定给a,a与10的内存地址解除绑定
func(x) # x=10的内存地址
print(x) # 10
2、对于全局定义的可变类型,可以在函数内直接修改
x = []
def func(a): # a=列表的内存地址
a.append(1111)
func(x) # x=列表的内存地址
print(x) # [1111]
3、使用 global,可以在函数内修改全局定义的不可变类型数据
x = 10
def func():
global x # 声明变量来自于全局
x = 666
func()
print(x) # 666
4、使用 nonlocal可以修改外层函数的局部空间变量
x = 10
def f1():
x = 11
def f2():
nonlocal x
x = 666
f2()
print(x) # x被f2修改成了666
f1() # x = 666
print(x) # 10